clang-format Mosh

Run clang-format over the Mosh source tree. This is a large change and
has been factored into its own commit for auditability. Reproduce it
with

    find . -name \*.cc -or -name \*.h | while read f; do clang-format -i --style=file $f; done
This commit is contained in:
Benjamin Barenblat
2023-08-07 21:53:48 -04:00
committed by Alex Chernyakhovsky
parent 0b15dc94fa
commit 3acaa1c4d3
77 changed files with 4838 additions and 4848 deletions
+28 -32
View File
@@ -32,14 +32,14 @@ extern "C" {
/* Return status codes: Negative return values indicate an error occurred. /* Return status codes: Negative return values indicate an error occurred.
* For full explanations of error values, consult the implementation's * For full explanations of error values, consult the implementation's
* documentation. */ * documentation. */
#define AE_SUCCESS ( 0) /* Indicates successful completion of call */ #define AE_SUCCESS ( 0 ) /* Indicates successful completion of call */
#define AE_INVALID (-1) /* Indicates bad tag during decryption */ #define AE_INVALID ( -1 ) /* Indicates bad tag during decryption */
#define AE_NOT_SUPPORTED (-2) /* Indicates unsupported option requested */ #define AE_NOT_SUPPORTED ( -2 ) /* Indicates unsupported option requested */
/* Flags: When data can be processed "incrementally", these flags are used /* Flags: When data can be processed "incrementally", these flags are used
* to indicate whether the submitted data is the last or not. */ * to indicate whether the submitted data is the last or not. */
#define AE_FINALIZE (1) /* This is the last of data */ #define AE_FINALIZE ( 1 ) /* This is the last of data */
#define AE_PENDING (0) /* More data of is coming */ #define AE_PENDING ( 0 ) /* More data of is coming */
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
@@ -55,10 +55,10 @@ typedef struct _ae_ctx ae_ctx;
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
ae_ctx* ae_allocate (void *misc); /* Allocate ae_ctx, set optional ptr */ ae_ctx* ae_allocate( void* misc ); /* Allocate ae_ctx, set optional ptr */
void ae_free (ae_ctx *ctx); /* Deallocate ae_ctx struct */ void ae_free( ae_ctx* ctx ); /* Deallocate ae_ctx struct */
int ae_clear (ae_ctx *ctx); /* Undo initialization */ int ae_clear( ae_ctx* ctx ); /* Undo initialization */
int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */ int ae_ctx_sizeof( void ); /* Return sizeof(ae_ctx) */
/* ae_allocate() allocates an ae_ctx structure, but does not initialize it. /* ae_allocate() allocates an ae_ctx structure, but does not initialize it.
* ae_free() deallocates an ae_ctx structure, but does not zeroize it. * ae_free() deallocates an ae_ctx structure, but does not zeroize it.
* ae_clear() zeroes sensitive values associated with an ae_ctx structure * ae_clear() zeroes sensitive values associated with an ae_ctx structure
@@ -72,11 +72,7 @@ int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_init(ae_ctx *ctx, int ae_init( ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len );
const void *key,
int key_len,
int nonce_len,
int tag_len);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Initialize an ae_ctx context structure. * Initialize an ae_ctx context structure.
@@ -95,15 +91,15 @@ int ae_init(ae_ctx *ctx,
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_encrypt(ae_ctx *ctx, int ae_encrypt( ae_ctx* ctx,
const void *nonce, const void* nonce,
const void *pt, const void* pt,
int pt_len, int pt_len,
const void *ad, const void* ad,
int ad_len, int ad_len,
void *ct, void* ct,
void *tag, void* tag,
int final); int final );
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Encrypt plaintext; provide for authentication of ciphertext/associated data. * Encrypt plaintext; provide for authentication of ciphertext/associated data.
@@ -132,15 +128,15 @@ int ae_encrypt(ae_ctx *ctx,
* *
* ----------------------------------------------------------------------- */ * ----------------------------------------------------------------------- */
int ae_decrypt(ae_ctx *ctx, int ae_decrypt( ae_ctx* ctx,
const void *nonce, const void* nonce,
const void *ct, const void* ct,
int ct_len, int ct_len,
const void *ad, const void* ad,
int ad_len, int ad_len,
void *pt, void* pt,
const void *tag, const void* tag,
int final); int final );
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* *
* Decrypt ciphertext; provide authenticity of plaintext and associated data. * Decrypt ciphertext; provide authenticity of plaintext and associated data.
+17 -19
View File
@@ -33,8 +33,8 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "src/util/fatal_assert.h"
#include "src/crypto/base64.h" #include "src/crypto/base64.h"
#include "src/util/fatal_assert.h"
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -60,27 +60,26 @@ static const unsigned char reverse[] = {
}; };
/* Reverse maps from an ASCII char to a base64 sixbit value. Returns > 0x3f on failure. */ /* Reverse maps from an ASCII char to a base64 sixbit value. Returns > 0x3f on failure. */
static unsigned char base64_char_to_sixbit(unsigned char c) static unsigned char base64_char_to_sixbit( unsigned char c )
{ {
return reverse[c]; return reverse[c];
} }
bool base64_decode( const char *b64, const size_t b64_len, bool base64_decode( const char* b64, const size_t b64_len, uint8_t* raw, size_t* raw_len )
uint8_t *raw, size_t *raw_len )
{ {
fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */
fatal_assert( *raw_len == 16 ); fatal_assert( *raw_len == 16 );
uint32_t bytes = 0; uint32_t bytes = 0;
for (int i = 0; i < 22; i++) { for ( int i = 0; i < 22; i++ ) {
unsigned char sixbit = base64_char_to_sixbit(*(b64++)); unsigned char sixbit = base64_char_to_sixbit( *( b64++ ) );
if (sixbit > 0x3f) { if ( sixbit > 0x3f ) {
return false; return false;
} }
bytes <<= 6; bytes <<= 6;
bytes |= sixbit; bytes |= sixbit;
/* write groups of 3 */ /* write groups of 3 */
if (i % 4 == 3) { if ( i % 4 == 3 ) {
raw[0] = bytes >> 16; raw[0] = bytes >> 16;
raw[1] = bytes >> 8; raw[1] = bytes >> 8;
raw[2] = bytes; raw[2] = bytes;
@@ -90,33 +89,32 @@ bool base64_decode( const char *b64, const size_t b64_len,
} }
/* last byte of output */ /* last byte of output */
*raw = bytes >> 4; *raw = bytes >> 4;
if (b64[0] != '=' || b64[1] != '=') { if ( b64[0] != '=' || b64[1] != '=' ) {
return false; return false;
} }
return true; return true;
} }
void base64_encode( const uint8_t *raw, const size_t raw_len, void base64_encode( const uint8_t* raw, const size_t raw_len, char* b64, const size_t b64_len )
char *b64, const size_t b64_len )
{ {
fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */ fatal_assert( b64_len == 24 ); /* only useful for Mosh keys */
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
/* first 15 bytes of input */ /* first 15 bytes of input */
for (int i = 0; i < 5; i++) { for ( int i = 0; i < 5; i++ ) {
uint32_t bytes = (raw[0] << 16) | (raw[1] << 8) | raw[2]; uint32_t bytes = ( raw[0] << 16 ) | ( raw[1] << 8 ) | raw[2];
b64[0] = table[(bytes >> 18) & 0x3f]; b64[0] = table[( bytes >> 18 ) & 0x3f];
b64[1] = table[(bytes >> 12) & 0x3f]; b64[1] = table[( bytes >> 12 ) & 0x3f];
b64[2] = table[(bytes >> 6) & 0x3f]; b64[2] = table[( bytes >> 6 ) & 0x3f];
b64[3] = table[(bytes) & 0x3f]; b64[3] = table[(bytes)&0x3f];
raw += 3; raw += 3;
b64 += 4; b64 += 4;
} }
/* last byte of input, last 4 of output */ /* last byte of input, last 4 of output */
uint8_t lastchar = *raw; uint8_t lastchar = *raw;
b64[0] = table[(lastchar >> 2) & 0x3f]; b64[0] = table[( lastchar >> 2 ) & 0x3f];
b64[1] = table[(lastchar << 4) & 0x3f]; b64[1] = table[( lastchar << 4 ) & 0x3f];
b64[2] = '='; b64[2] = '=';
b64[3] = '='; b64[3] = '=';
} }
+2 -4
View File
@@ -32,8 +32,6 @@
#include <cstdint> #include <cstdint>
bool base64_decode( const char *b64, const size_t b64_len, bool base64_decode( const char* b64, const size_t b64_len, uint8_t* raw, size_t* raw_len );
uint8_t *raw, size_t *raw_len );
void base64_encode( const uint8_t *raw, const size_t raw_len, void base64_encode( const uint8_t* raw, const size_t raw_len, char* b64, const size_t b64_len );
char *b64, const size_t b64_len );
+40 -45
View File
@@ -37,12 +37,12 @@
#if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64 #if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64
# if defined(HAVE_ENDIAN_H) #if defined( HAVE_ENDIAN_H )
# include <endian.h> #include <endian.h>
# elif defined(HAVE_SYS_ENDIAN_H) #elif defined( HAVE_SYS_ENDIAN_H )
# include <sys/types.h> #include <sys/endian.h>
# include <sys/endian.h> #include <sys/types.h>
# endif #endif
#if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64 #if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64
#define be64toh betoh64 #define be64toh betoh64
@@ -50,11 +50,11 @@
#endif #endif
#elif HAVE_OSX_SWAP #elif HAVE_OSX_SWAP
# include <libkern/OSByteOrder.h> #include <libkern/OSByteOrder.h>
# define htobe64 OSSwapHostToBigInt64 #define htobe64 OSSwapHostToBigInt64
# define be64toh OSSwapBigToHostInt64 #define be64toh OSSwapBigToHostInt64
# define htobe16 OSSwapHostToBigInt16 #define htobe16 OSSwapHostToBigInt16
# define be16toh OSSwapBigToHostInt16 #define be16toh OSSwapBigToHostInt16
#else #else
@@ -70,60 +70,55 @@
/* Use unions rather than casts, to comply with strict aliasing rules. */ /* Use unions rather than casts, to comply with strict aliasing rules. */
inline uint64_t htobe64( uint64_t x ) { inline uint64_t htobe64( uint64_t x )
uint8_t xs[ 8 ] = { {
static_cast<uint8_t>( ( x >> 56 ) & 0xFF ), uint8_t xs[8] = { static_cast<uint8_t>( ( x >> 56 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 48 ) & 0xFF ), static_cast<uint8_t>( ( x >> 48 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 40 ) & 0xFF ), static_cast<uint8_t>( ( x >> 40 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 32 ) & 0xFF ), static_cast<uint8_t>( ( x >> 32 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 24 ) & 0xFF ), static_cast<uint8_t>( ( x >> 24 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 16 ) & 0xFF ), static_cast<uint8_t>( ( x >> 16 ) & 0xFF ),
static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), static_cast<uint8_t>( ( x >> 8 ) & 0xFF ),
static_cast<uint8_t>( ( x ) & 0xFF ) }; static_cast<uint8_t>( (x)&0xFF ) };
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint64_t *p64; const uint64_t* p64;
} u; } u;
u.p8 = xs; u.p8 = xs;
return *u.p64; return *u.p64;
} }
inline uint64_t be64toh( uint64_t x ) { inline uint64_t be64toh( uint64_t x )
{
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint64_t *p64; const uint64_t* p64;
} u; } u;
u.p64 = &x; u.p64 = &x;
return ( uint64_t( u.p8[ 0 ] ) << 56 ) return ( uint64_t( u.p8[0] ) << 56 ) | ( uint64_t( u.p8[1] ) << 48 ) | ( uint64_t( u.p8[2] ) << 40 )
| ( uint64_t( u.p8[ 1 ] ) << 48 ) | ( uint64_t( u.p8[3] ) << 32 ) | ( uint64_t( u.p8[4] ) << 24 ) | ( uint64_t( u.p8[5] ) << 16 )
| ( uint64_t( u.p8[ 2 ] ) << 40 ) | ( uint64_t( u.p8[6] ) << 8 ) | ( uint64_t( u.p8[7] ) );
| ( uint64_t( u.p8[ 3 ] ) << 32 )
| ( uint64_t( u.p8[ 4 ] ) << 24 )
| ( uint64_t( u.p8[ 5 ] ) << 16 )
| ( uint64_t( u.p8[ 6 ] ) << 8 )
| ( uint64_t( u.p8[ 7 ] ) );
} }
inline uint16_t htobe16( uint16_t x ) { inline uint16_t htobe16( uint16_t x )
uint8_t xs[ 2 ] = { {
static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), uint8_t xs[2] = { static_cast<uint8_t>( ( x >> 8 ) & 0xFF ), static_cast<uint8_t>( (x)&0xFF ) };
static_cast<uint8_t>( ( x ) & 0xFF ) };
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint16_t *p16; const uint16_t* p16;
} u; } u;
u.p8 = xs; u.p8 = xs;
return *u.p16; return *u.p16;
} }
inline uint16_t be16toh( uint16_t x ) { inline uint16_t be16toh( uint16_t x )
{
union { union {
const uint8_t *p8; const uint8_t* p8;
const uint16_t *p16; const uint16_t* p16;
} u; } u;
u.p16 = &x; u.p16 = &x;
return ( uint16_t( u.p8[ 0 ] ) << 8 ) return ( uint16_t( u.p8[0] ) << 8 ) | ( uint16_t( u.p8[1] ) );
| ( uint16_t( u.p8[ 1 ] ) );
} }
#endif #endif
+46 -49
View File
@@ -39,23 +39,22 @@
#include <sys/resource.h> #include <sys/resource.h>
#include "src/crypto/base64.h"
#include "src/crypto/byteorder.h" #include "src/crypto/byteorder.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/crypto/base64.h"
#include "src/util/fatal_assert.h"
#include "src/crypto/prng.h" #include "src/crypto/prng.h"
#include "src/util/fatal_assert.h"
using namespace Crypto; using namespace Crypto;
long int myatoi( const char *str ) long int myatoi( const char* str )
{ {
char *end; char* end;
errno = 0; errno = 0;
long int ret = strtol( str, &end, 10 ); long int ret = strtol( str, &end, 10 );
if ( ( errno != 0 ) if ( ( errno != 0 ) || ( end != str + strlen( str ) ) ) {
|| ( end != str + strlen( str ) ) ) {
throw CryptoException( "Bad integer." ); throw CryptoException( "Bad integer." );
} }
@@ -72,16 +71,14 @@ uint64_t Crypto::unique( void )
return rv; return rv;
} }
AlignedBuffer::AlignedBuffer( size_t len, const char *data ) AlignedBuffer::AlignedBuffer( size_t len, const char* data ) : m_len( len ), m_allocated( NULL ), m_data( NULL )
: m_len( len ), m_allocated( NULL ), m_data( NULL )
{ {
size_t alloc_len = len ? len : 1; size_t alloc_len = len ? len : 1;
#if defined(HAVE_POSIX_MEMALIGN) #if defined( HAVE_POSIX_MEMALIGN )
if ( ( 0 != posix_memalign( &m_allocated, 16, alloc_len ) ) if ( ( 0 != posix_memalign( &m_allocated, 16, alloc_len ) ) || ( m_allocated == NULL ) ) {
|| ( m_allocated == NULL ) ) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
m_data = (char *) m_allocated; m_data = (char*)m_allocated;
#else #else
/* malloc() a region 15 bytes larger than we need, and find /* malloc() a region 15 bytes larger than we need, and find
@@ -91,15 +88,15 @@ AlignedBuffer::AlignedBuffer( size_t len, const char *data )
throw std::bad_alloc(); throw std::bad_alloc();
} }
uintptr_t iptr = (uintptr_t) m_allocated; uintptr_t iptr = (uintptr_t)m_allocated;
if ( iptr & 0xF ) { if ( iptr & 0xF ) {
iptr += 16 - ( iptr & 0xF ); iptr += 16 - ( iptr & 0xF );
} }
assert( !( iptr & 0xF ) ); assert( !( iptr & 0xF ) );
assert( iptr >= (uintptr_t) m_allocated ); assert( iptr >= (uintptr_t)m_allocated );
assert( iptr <= ( 15 + (uintptr_t) m_allocated ) ); assert( iptr <= ( 15 + (uintptr_t)m_allocated ) );
m_data = (char *) iptr; m_data = (char*)iptr;
#endif /* !defined(HAVE_POSIX_MEMALIGN) */ #endif /* !defined(HAVE_POSIX_MEMALIGN) */
@@ -136,32 +133,28 @@ Base64Key::Base64Key()
PRNG().fill( key, sizeof( key ) ); PRNG().fill( key, sizeof( key ) );
} }
Base64Key::Base64Key(PRNG &prng) Base64Key::Base64Key( PRNG& prng )
{ {
prng.fill( key, sizeof( key ) ); prng.fill( key, sizeof( key ) );
} }
std::string Base64Key::printable_key( void ) const std::string Base64Key::printable_key( void ) const
{ {
char base64[ 24 ]; char base64[24];
base64_encode( key, 16, base64, 24 ); base64_encode( key, 16, base64, 24 );
if ( (base64[ 23 ] != '=') if ( ( base64[23] != '=' ) || ( base64[22] != '=' ) ) {
|| (base64[ 22 ] != '=') ) {
throw CryptoException( std::string( "Unexpected output from base64_encode: " ) + std::string( base64, 24 ) ); throw CryptoException( std::string( "Unexpected output from base64_encode: " ) + std::string( base64, 24 ) );
} }
base64[ 22 ] = 0; base64[22] = 0;
return std::string( base64 ); return std::string( base64 );
} }
Session::Session( Base64Key s_key ) Session::Session( Base64Key s_key )
: key( s_key ), ctx_buf( ae_ctx_sizeof() ), : key( s_key ), ctx_buf( ae_ctx_sizeof() ), ctx( (ae_ctx*)ctx_buf.data() ), blocks_encrypted( 0 ),
ctx( (ae_ctx *)ctx_buf.data() ), blocks_encrypted( 0 ), plaintext_buffer( RECEIVE_MTU ), ciphertext_buffer( RECEIVE_MTU ), nonce_buffer( Nonce::NONCE_LEN )
plaintext_buffer( RECEIVE_MTU ),
ciphertext_buffer( RECEIVE_MTU ),
nonce_buffer( Nonce::NONCE_LEN )
{ {
if ( AE_SUCCESS != ae_init( ctx, key.data(), 16, 12, 16 ) ) { if ( AE_SUCCESS != ae_init( ctx, key.data(), 16, 12, 16 ) ) {
throw CryptoException( "Could not initialize AES-OCB context." ); throw CryptoException( "Could not initialize AES-OCB context." );
@@ -188,7 +181,7 @@ uint64_t Nonce::val( void ) const
return be64toh( ret ); return be64toh( ret );
} }
Nonce::Nonce( const char *s_bytes, size_t len ) Nonce::Nonce( const char* s_bytes, size_t len )
{ {
if ( len != 8 ) { if ( len != 8 ) {
throw CryptoException( "Nonce representation must be 8 octets long." ); throw CryptoException( "Nonce representation must be 8 octets long." );
@@ -198,7 +191,7 @@ Nonce::Nonce( const char *s_bytes, size_t len )
memcpy( bytes + 4, s_bytes, 8 ); memcpy( bytes + 4, s_bytes, 8 );
} }
const std::string Session::encrypt( const Message & plaintext ) const std::string Session::encrypt( const Message& plaintext )
{ {
const size_t pt_len = plaintext.text.size(); const size_t pt_len = plaintext.text.size();
const int ciphertext_len = pt_len + 16; const int ciphertext_len = pt_len + 16;
@@ -209,15 +202,16 @@ const std::string Session::encrypt( const Message & plaintext )
memcpy( plaintext_buffer.data(), plaintext.text.data(), pt_len ); memcpy( plaintext_buffer.data(), plaintext.text.data(), pt_len );
memcpy( nonce_buffer.data(), plaintext.nonce.data(), Nonce::NONCE_LEN ); memcpy( nonce_buffer.data(), plaintext.nonce.data(), Nonce::NONCE_LEN );
if ( ciphertext_len != ae_encrypt( ctx, /* ctx */ if ( ciphertext_len
nonce_buffer.data(), /* nonce */ != ae_encrypt( ctx, /* ctx */
plaintext_buffer.data(), /* pt */ nonce_buffer.data(), /* nonce */
pt_len, /* pt_len */ plaintext_buffer.data(), /* pt */
NULL, /* ad */ pt_len, /* pt_len */
0, /* ad_len */ NULL, /* ad */
ciphertext_buffer.data(), /* ct */ 0, /* ad_len */
NULL, /* tag */ ciphertext_buffer.data(), /* ct */
AE_FINALIZE ) ) { /* final */ NULL, /* tag */
AE_FINALIZE ) ) { /* final */
throw CryptoException( "ae_encrypt() returned error." ); throw CryptoException( "ae_encrypt() returned error." );
} }
@@ -248,7 +242,7 @@ const std::string Session::encrypt( const Message & plaintext )
return plaintext.nonce.cc_str() + text; return plaintext.nonce.cc_str() + text;
} }
const Message Session::decrypt( const char *str, size_t len ) const Message Session::decrypt( const char* str, size_t len )
{ {
if ( len < 24 ) { if ( len < 24 ) {
throw CryptoException( "Ciphertext must contain nonce and tag." ); throw CryptoException( "Ciphertext must contain nonce and tag." );
@@ -269,15 +263,16 @@ const Message Session::decrypt( const char *str, size_t len )
memcpy( ciphertext_buffer.data(), str + 8, body_len ); memcpy( ciphertext_buffer.data(), str + 8, body_len );
memcpy( nonce_buffer.data(), nonce.data(), Nonce::NONCE_LEN ); memcpy( nonce_buffer.data(), nonce.data(), Nonce::NONCE_LEN );
if ( pt_len != ae_decrypt( ctx, /* ctx */ if ( pt_len
nonce_buffer.data(), /* nonce */ != ae_decrypt( ctx, /* ctx */
ciphertext_buffer.data(), /* ct */ nonce_buffer.data(), /* nonce */
body_len, /* ct_len */ ciphertext_buffer.data(), /* ct */
NULL, /* ad */ body_len, /* ct_len */
0, /* ad_len */ NULL, /* ad */
plaintext_buffer.data(), /* pt */ 0, /* ad_len */
NULL, /* tag */ plaintext_buffer.data(), /* pt */
AE_FINALIZE ) ) { /* final */ NULL, /* tag */
AE_FINALIZE ) ) { /* final */
throw CryptoException( "Packet failed integrity check." ); throw CryptoException( "Packet failed integrity check." );
} }
@@ -290,7 +285,8 @@ static rlim_t saved_core_rlimit;
/* Disable dumping core, as a precaution to avoid saving sensitive data /* Disable dumping core, as a precaution to avoid saving sensitive data
to disk. */ to disk. */
void Crypto::disable_dumping_core( void ) { void Crypto::disable_dumping_core( void )
{
struct rlimit limit; struct rlimit limit;
if ( 0 != getrlimit( RLIMIT_CORE, &limit ) ) { if ( 0 != getrlimit( RLIMIT_CORE, &limit ) ) {
/* We don't throw CryptoException because this is called very early /* We don't throw CryptoException because this is called very early
@@ -307,7 +303,8 @@ void Crypto::disable_dumping_core( void ) {
} }
} }
void Crypto::reenable_dumping_core( void ) { void Crypto::reenable_dumping_core( void )
{
/* Silent failure is safe. */ /* Silent failure is safe. */
struct rlimit limit; struct rlimit limit;
if ( 0 == getrlimit( RLIMIT_CORE, &limit ) ) { if ( 0 == getrlimit( RLIMIT_CORE, &limit ) ) {
+91 -94
View File
@@ -41,126 +41,123 @@
#include <exception> #include <exception>
#include <string> #include <string>
long int myatoi( const char* str );
long int myatoi( const char *str );
class PRNG; class PRNG;
namespace Crypto { namespace Crypto {
class CryptoException : public std::exception { class CryptoException : public std::exception
public: {
std::string text; public:
bool fatal; std::string text;
CryptoException( std::string s_text, bool s_fatal = false ) bool fatal;
: text( s_text ), fatal( s_fatal ) {}; CryptoException( std::string s_text, bool s_fatal = false ) : text( s_text ), fatal( s_fatal ) {};
const char *what() const throw () { return text.c_str(); } const char* what() const throw() { return text.c_str(); }
~CryptoException() throw () {} ~CryptoException() throw() {}
}; };
/* /*
* OCB (and other algorithms) require a source of nonce/sequence * OCB (and other algorithms) require a source of nonce/sequence
* numbers that never repeats its output. Enforce that with this * numbers that never repeats its output. Enforce that with this
* function. * function.
*/ */
uint64_t unique( void ); uint64_t unique( void );
/* 16-byte-aligned buffer, with length. */ /* 16-byte-aligned buffer, with length. */
class AlignedBuffer { class AlignedBuffer
private: {
size_t m_len; private:
void *m_allocated; size_t m_len;
char *m_data; void* m_allocated;
char* m_data;
public: public:
AlignedBuffer( size_t len, const char *data = NULL ); AlignedBuffer( size_t len, const char* data = NULL );
~AlignedBuffer() { ~AlignedBuffer() { free( m_allocated ); }
free( m_allocated );
}
char * data( void ) const { return m_data; } char* data( void ) const { return m_data; }
size_t len( void ) const { return m_len; } size_t len( void ) const { return m_len; }
private: private:
/* Not implemented */ /* Not implemented */
AlignedBuffer( const AlignedBuffer & ); AlignedBuffer( const AlignedBuffer& );
AlignedBuffer & operator=( const AlignedBuffer & ); AlignedBuffer& operator=( const AlignedBuffer& );
}; };
class Base64Key { class Base64Key
private: {
unsigned char key[ 16 ]; private:
unsigned char key[16];
public: public:
Base64Key(); /* random key */ Base64Key(); /* random key */
Base64Key(PRNG &prng); Base64Key( PRNG& prng );
Base64Key( std::string printable_key ); Base64Key( std::string printable_key );
std::string printable_key( void ) const; std::string printable_key( void ) const;
unsigned char *data( void ) { return key; } unsigned char* data( void ) { return key; }
}; };
class Nonce { class Nonce
public: {
static const int NONCE_LEN = 12; public:
static const int NONCE_LEN = 12;
private: private:
char bytes[ NONCE_LEN ]; char bytes[NONCE_LEN];
public: public:
Nonce( uint64_t val ); Nonce( uint64_t val );
Nonce( const char *s_bytes, size_t len ); Nonce( const char* s_bytes, size_t len );
std::string cc_str( void ) const { return std::string( bytes + 4, 8 ); } std::string cc_str( void ) const { return std::string( bytes + 4, 8 ); }
const char *data( void ) const { return bytes; } const char* data( void ) const { return bytes; }
uint64_t val( void ) const; uint64_t val( void ) const;
}; };
class Message { class Message
public: {
const Nonce nonce; public:
const std::string text; const Nonce nonce;
const std::string text;
Message( const char *nonce_bytes, size_t nonce_len, Message( const char* nonce_bytes, size_t nonce_len, const char* text_bytes, size_t text_len )
const char *text_bytes, size_t text_len ) : nonce( nonce_bytes, nonce_len ), text( text_bytes, text_len )
: nonce( nonce_bytes, nonce_len ), {}
text( text_bytes, text_len ) {}
Message( const Nonce & s_nonce, const std::string & s_text ) Message( const Nonce& s_nonce, const std::string& s_text ) : nonce( s_nonce ), text( s_text ) {}
: nonce( s_nonce ), };
text( s_text ) {}
};
class Session { class Session
private: {
Base64Key key; private:
AlignedBuffer ctx_buf; Base64Key key;
ae_ctx *ctx; AlignedBuffer ctx_buf;
uint64_t blocks_encrypted; ae_ctx* ctx;
uint64_t blocks_encrypted;
AlignedBuffer plaintext_buffer; AlignedBuffer plaintext_buffer;
AlignedBuffer ciphertext_buffer; AlignedBuffer ciphertext_buffer;
AlignedBuffer nonce_buffer; AlignedBuffer nonce_buffer;
public: public:
static const int RECEIVE_MTU = 2048; static const int RECEIVE_MTU = 2048;
/* Overhead (not counting the nonce, which is handled by network transport) */ /* Overhead (not counting the nonce, which is handled by network transport) */
static const int ADDED_BYTES = 16 /* final OCB block */; static const int ADDED_BYTES = 16 /* final OCB block */;
Session( Base64Key s_key ); Session( Base64Key s_key );
~Session(); ~Session();
const std::string encrypt( const Message & plaintext ); const std::string encrypt( const Message& plaintext );
const Message decrypt( const char *str, size_t len ); const Message decrypt( const char* str, size_t len );
const Message decrypt( const std::string & ciphertext ) { const Message decrypt( const std::string& ciphertext ) { return decrypt( ciphertext.data(), ciphertext.size() ); }
return decrypt( ciphertext.data(), ciphertext.size() );
}
Session( const Session & ); Session( const Session& );
Session & operator=( const Session & ); Session& operator=( const Session& );
}; };
void disable_dumping_core( void ); void disable_dumping_core( void );
void reenable_dumping_core( void ); void reenable_dumping_core( void );
} }
#endif #endif
+99 -78
View File
@@ -1,188 +1,209 @@
#include "src/include/config.h"
#include "src/crypto/ae.h" #include "src/crypto/ae.h"
#include "src/include/config.h"
#include <cstring> #include <cstring>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <openssl/evp.h> #include <openssl/evp.h>
struct _ae_ctx { struct _ae_ctx
EVP_CIPHER_CTX *enc_ctx; {
EVP_CIPHER_CTX *dec_ctx; EVP_CIPHER_CTX* enc_ctx;
EVP_CIPHER_CTX* dec_ctx;
int tag_len; int tag_len;
}; };
int ae_clear(ae_ctx* ctx) { int ae_clear( ae_ctx* ctx )
EVP_CIPHER_CTX_free(ctx->enc_ctx); {
EVP_CIPHER_CTX_free(ctx->dec_ctx); EVP_CIPHER_CTX_free( ctx->enc_ctx );
OPENSSL_cleanse(ctx, sizeof(*ctx)); EVP_CIPHER_CTX_free( ctx->dec_ctx );
OPENSSL_cleanse( ctx, sizeof( *ctx ) );
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_ctx_sizeof() { int ae_ctx_sizeof()
return sizeof(_ae_ctx); {
return sizeof( _ae_ctx );
} }
// If direction is 1, initializes encryption. If 0, initializes // If direction is 1, initializes encryption. If 0, initializes
// decryption. See the documentation of EVP_CipherInit_ex // decryption. See the documentation of EVP_CipherInit_ex
static int ae_evp_cipher_init(EVP_CIPHER_CTX **in_ctx, int direction, static int ae_evp_cipher_init( EVP_CIPHER_CTX** in_ctx,
const unsigned char *key, int direction,
int nonce_len, int tag_len) { const unsigned char* key,
int nonce_len,
int tag_len )
{
// Create an OpenSSL EVP context. It does not yet have any specific // Create an OpenSSL EVP context. It does not yet have any specific
// cipher associated with it. // cipher associated with it.
if (!(*in_ctx = EVP_CIPHER_CTX_new())) { if ( !( *in_ctx = EVP_CIPHER_CTX_new() ) ) {
return -3; return -3;
} }
EVP_CIPHER_CTX *ctx = *in_ctx; EVP_CIPHER_CTX* ctx = *in_ctx;
// Although OCB-AES has the same initialization process between // Although OCB-AES has the same initialization process between
// encryption and decryption, an EVP_CIPHER_CTX must be initialized // encryption and decryption, an EVP_CIPHER_CTX must be initialized
// for a specific direction. // for a specific direction.
if (EVP_CipherInit_ex(ctx, EVP_aes_128_ocb(), if ( EVP_CipherInit_ex( ctx,
/*impl=*/NULL, /*key=*/key, /*iv=*/NULL, EVP_aes_128_ocb(),
direction) != 1) { /*impl=*/NULL,
/*key=*/key,
/*iv=*/NULL,
direction )
!= 1 ) {
return -3; return -3;
} }
// Attempt to set the nonce length. If it fails, the length must not // Attempt to set the nonce length. If it fails, the length must not
// be supported. However, that should have been handled by the // be supported. However, that should have been handled by the
// pre-condition check above. // pre-condition check above.
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, if ( EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, NULL ) != 1 ) {
nonce_len, NULL) != 1) {
return -3; return -3;
} }
// A NULL tag length means that EVP_CTRL_AEAD_SET_TAG is only being // A NULL tag length means that EVP_CTRL_AEAD_SET_TAG is only being
// used to set the length // used to set the length
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL ) != 1 ) {
tag_len, NULL) != 1) {
return -3; return -3;
} }
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_init(ae_ctx *ctx, const void *key, int key_len, int nonce_len, int ae_init( ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len )
int tag_len) { {
// Pre-condition: Only nonces of length 12 are supported. The // Pre-condition: Only nonces of length 12 are supported. The
// documentation of `ae_init` in ae.h specifies that `ctx` is // documentation of `ae_init` in ae.h specifies that `ctx` is
// untouched if an invalid configuration is requested. Delegating // untouched if an invalid configuration is requested. Delegating
// this to OpenSSL would happen too late; `ctx` has already been // this to OpenSSL would happen too late; `ctx` has already been
// modified. // modified.
if (nonce_len != 12) { if ( nonce_len != 12 ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
// Pre-condition: Only AES-128 is supported. // Pre-condition: Only AES-128 is supported.
if (key_len != 16) { if ( key_len != 16 ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
int r = AE_SUCCESS; int r = AE_SUCCESS;
if ((r = ae_evp_cipher_init(&ctx->enc_ctx, 1, if ( ( r = ae_evp_cipher_init(
reinterpret_cast<const unsigned char *>(key), &ctx->enc_ctx, 1, reinterpret_cast<const unsigned char*>( key ), nonce_len, tag_len ) )
nonce_len, tag_len))!= AE_SUCCESS) { != AE_SUCCESS ) {
return r; return r;
} }
if ((r = ae_evp_cipher_init(&ctx->dec_ctx, 0, if ( ( r = ae_evp_cipher_init(
reinterpret_cast<const unsigned char *>(key), &ctx->dec_ctx, 0, reinterpret_cast<const unsigned char*>( key ), nonce_len, tag_len ) )
nonce_len, tag_len)) != AE_SUCCESS) { != AE_SUCCESS ) {
return r; return r;
} }
ctx->tag_len = tag_len; ctx->tag_len = tag_len;
return AE_SUCCESS; return AE_SUCCESS;
} }
int ae_encrypt(ae_ctx *ctx, const void *nonce_ptr, const void *pt_ptr, int ae_encrypt( ae_ctx* ctx,
int pt_len, const void *ad_ptr, int ad_len, void *ct_ptr, const void* nonce_ptr,
void *tag, int final) { const void* pt_ptr,
const unsigned char *nonce = int pt_len,
reinterpret_cast<const unsigned char *>(nonce_ptr); const void* ad_ptr,
const unsigned char *pt = reinterpret_cast<const unsigned char *>(pt_ptr); int ad_len,
const unsigned char* ad = reinterpret_cast<const unsigned char *>(ad_ptr); void* ct_ptr,
unsigned char* ct = reinterpret_cast<unsigned char *>(ct_ptr); void* tag,
int final )
{
const unsigned char* nonce = reinterpret_cast<const unsigned char*>( nonce_ptr );
const unsigned char* pt = reinterpret_cast<const unsigned char*>( pt_ptr );
const unsigned char* ad = reinterpret_cast<const unsigned char*>( ad_ptr );
unsigned char* ct = reinterpret_cast<unsigned char*>( ct_ptr );
// Streaming mode is not supported; nonce must always be provided. // Streaming mode is not supported; nonce must always be provided.
if (final != AE_FINALIZE) { if ( final != AE_FINALIZE ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (nonce == NULL) { if ( nonce == NULL ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (EVP_EncryptInit_ex(ctx->enc_ctx, /*type=*/NULL, /*impl=*/NULL, if ( EVP_EncryptInit_ex( ctx->enc_ctx,
/*key=*/NULL, nonce) != 1) { /*type=*/NULL,
/*impl=*/NULL,
/*key=*/NULL,
nonce )
!= 1 ) {
return -3; return -3;
} }
int len = 0; int len = 0;
if (ad != NULL && ad_len > 0 && if ( ad != NULL && ad_len > 0 && EVP_EncryptUpdate( ctx->enc_ctx, /*out=*/NULL, &len, ad, ad_len ) != 1 ) {
EVP_EncryptUpdate(ctx->enc_ctx, /*out=*/NULL, &len, ad, ad_len) != 1) { return -3;
return -3;
} }
len = 0; len = 0;
if (pt != NULL && pt_len > 0 && if ( pt != NULL && pt_len > 0 && EVP_EncryptUpdate( ctx->enc_ctx, ct, &len, pt, pt_len ) != 1 ) {
EVP_EncryptUpdate(ctx->enc_ctx, ct, &len, pt, pt_len) != 1) {
return -3; return -3;
} }
int ciphertext_len = len; int ciphertext_len = len;
if (EVP_EncryptFinal_ex(ctx->enc_ctx, ct + ciphertext_len, &len) != 1) { if ( EVP_EncryptFinal_ex( ctx->enc_ctx, ct + ciphertext_len, &len ) != 1 ) {
return -3; return -3;
} }
ciphertext_len += len; ciphertext_len += len;
// If `tag` is provided, the authentication tag goes // If `tag` is provided, the authentication tag goes
// there. Otherwise, it is appended after the ciphertext. // there. Otherwise, it is appended after the ciphertext.
void *tag_location = tag != NULL ? tag : ct + ciphertext_len; void* tag_location = tag != NULL ? tag : ct + ciphertext_len;
if (EVP_CIPHER_CTX_ctrl(ctx->enc_ctx, EVP_CTRL_AEAD_GET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx->enc_ctx, EVP_CTRL_AEAD_GET_TAG, ctx->tag_len, tag_location ) != 1 ) {
ctx->tag_len, tag_location) != 1) {
return -3; return -3;
} }
if (tag == NULL) { if ( tag == NULL ) {
ciphertext_len += ctx->tag_len; ciphertext_len += ctx->tag_len;
} }
return ciphertext_len; return ciphertext_len;
} }
int ae_decrypt( ae_ctx* ctx,
int ae_decrypt(ae_ctx *ctx, const void *nonce_ptr, const void *ct_ptr, const void* nonce_ptr,
int ct_len, const void *ad_ptr, int ad_len, void *pt_ptr, const void* ct_ptr,
const void *tag, int final) { int ct_len,
const unsigned char *nonce = const void* ad_ptr,
reinterpret_cast<const unsigned char *>(nonce_ptr); int ad_len,
const unsigned char *ct = reinterpret_cast<const unsigned char *>(ct_ptr); void* pt_ptr,
const unsigned char* ad = reinterpret_cast<const unsigned char *>(ad_ptr); const void* tag,
unsigned char* pt = reinterpret_cast<unsigned char *>(pt_ptr); int final )
if (ct_len < ctx->tag_len) { {
const unsigned char* nonce = reinterpret_cast<const unsigned char*>( nonce_ptr );
const unsigned char* ct = reinterpret_cast<const unsigned char*>( ct_ptr );
const unsigned char* ad = reinterpret_cast<const unsigned char*>( ad_ptr );
unsigned char* pt = reinterpret_cast<unsigned char*>( pt_ptr );
if ( ct_len < ctx->tag_len ) {
return AE_INVALID; return AE_INVALID;
} }
// If an external tag is not provided, then the tag is assumed to be // If an external tag is not provided, then the tag is assumed to be
// the final bytes of the cipher text. Subtract it off now so the // the final bytes of the cipher text. Subtract it off now so the
// plaintext does not accidentally try to decrypt the AEAD tag (and // plaintext does not accidentally try to decrypt the AEAD tag (and
// then cause an authentication failure). // then cause an authentication failure).
if (tag == NULL) { if ( tag == NULL ) {
ct_len -= ctx->tag_len; ct_len -= ctx->tag_len;
} }
// Like encryption, nonce must always be provided and streaming is not supported. // Like encryption, nonce must always be provided and streaming is not supported.
if (final != AE_FINALIZE) { if ( final != AE_FINALIZE ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (nonce == NULL) { if ( nonce == NULL ) {
return AE_NOT_SUPPORTED; return AE_NOT_SUPPORTED;
} }
if (EVP_DecryptInit_ex(ctx->dec_ctx, /*type=*/NULL, /*impl=*/NULL, if ( EVP_DecryptInit_ex( ctx->dec_ctx,
/*key=*/NULL, nonce) != 1) { /*type=*/NULL,
/*impl=*/NULL,
/*key=*/NULL,
nonce )
!= 1 ) {
return -3; return -3;
} }
int len = 0; int len = 0;
if (ad != NULL && ad_len > 0 && if ( ad != NULL && ad_len > 0 && EVP_DecryptUpdate( ctx->dec_ctx, /*out=*/NULL, &len, ad, ad_len ) != 1 ) {
EVP_DecryptUpdate(ctx->dec_ctx, /*out=*/NULL, &len, ad, ad_len) != 1) { return -3;
return -3;
} }
len = 0; len = 0;
if (ct != NULL && ct_len > 0 && if ( ct != NULL && ct_len > 0 && EVP_DecryptUpdate( ctx->dec_ctx, pt, &len, ct, ct_len ) != 1 ) {
EVP_DecryptUpdate(ctx->dec_ctx, pt, &len, ct, ct_len) != 1) {
return -3; return -3;
} }
int plaintext_len = len; int plaintext_len = len;
// If `tag` is provided, the authentication is read from // If `tag` is provided, the authentication is read from
// there. Otherwise, it's the last bytes of the ciphertext. (This is // there. Otherwise, it's the last bytes of the ciphertext. (This is
// a convention, not a requirement of OCB mode). // a convention, not a requirement of OCB mode).
const void *tag_location = tag != NULL ? tag : ct + ct_len; const void* tag_location = tag != NULL ? tag : ct + ct_len;
if (EVP_CIPHER_CTX_ctrl(ctx->dec_ctx, EVP_CTRL_AEAD_SET_TAG, if ( EVP_CIPHER_CTX_ctrl( ctx->dec_ctx, EVP_CTRL_AEAD_SET_TAG, ctx->tag_len, (void*)tag_location ) != 1 ) {
ctx->tag_len, (void *)tag_location) != 1) {
return -3; return -3;
} }
if (EVP_DecryptFinal_ex(ctx->dec_ctx, pt + plaintext_len, &len) != 1) { if ( EVP_DecryptFinal_ex( ctx->dec_ctx, pt + plaintext_len, &len ) != 1 ) {
return AE_INVALID; return AE_INVALID;
} }
plaintext_len += len; plaintext_len += len;
+15 -10
View File
@@ -47,41 +47,46 @@ static const char rdev[] = "/dev/urandom";
using namespace Crypto; using namespace Crypto;
class PRNG { class PRNG
private: {
private:
std::ifstream randfile; std::ifstream randfile;
/* unimplemented to satisfy -Weffc++ */ /* unimplemented to satisfy -Weffc++ */
PRNG( const PRNG & ); PRNG( const PRNG& );
PRNG & operator=( const PRNG & ); PRNG& operator=( const PRNG& );
public: public:
PRNG() : randfile( rdev, std::ifstream::in | std::ifstream::binary ) {} PRNG() : randfile( rdev, std::ifstream::in | std::ifstream::binary ) {}
void fill( void *dest, size_t size ) { void fill( void* dest, size_t size )
{
if ( 0 == size ) { if ( 0 == size ) {
return; return;
} }
randfile.read( static_cast<char *>( dest ), size ); randfile.read( static_cast<char*>( dest ), size );
if ( !randfile ) { if ( !randfile ) {
throw CryptoException( "Could not read from " + std::string( rdev ) ); throw CryptoException( "Could not read from " + std::string( rdev ) );
} }
} }
uint8_t uint8() { uint8_t uint8()
{
uint8_t x; uint8_t x;
fill( &x, 1 ); fill( &x, 1 );
return x; return x;
} }
uint32_t uint32() { uint32_t uint32()
{
uint32_t x; uint32_t x;
fill( &x, 4 ); fill( &x, 4 );
return x; return x;
} }
uint64_t uint64() { uint64_t uint64()
{
uint64_t x; uint64_t x;
fill( &x, 8 ); fill( &x, 8 );
return x; return x;
+23 -25
View File
@@ -53,41 +53,41 @@
#include <util.h> #include <util.h>
#endif #endif
#include "src/util/swrite.h" #include "src/frontend/terminaloverlay.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/frontend/terminaloverlay.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "src/util/swrite.h"
const int ITERATIONS = 100000; const int ITERATIONS = 100000;
using namespace Terminal; using namespace Terminal;
int main( int argc, char **argv ) int main( int argc, char** argv )
{ {
try { try {
int fbmod = 0; int fbmod = 0;
int width = 80, height = 24; int width = 80, height = 24;
int iterations = ITERATIONS; int iterations = ITERATIONS;
if (argc > 1) { if ( argc > 1 ) {
iterations = atoi(argv[1]); iterations = atoi( argv[1] );
if (iterations < 1 || iterations > 1000000000) { if ( iterations < 1 || iterations > 1000000000 ) {
fprintf(stderr, "bogus iteration count\n"); fprintf( stderr, "bogus iteration count\n" );
exit(1); exit( 1 );
} }
} }
if (argc > 3) { if ( argc > 3 ) {
width = atoi(argv[2]); width = atoi( argv[2] );
height = atoi(argv[3]); height = atoi( argv[3] );
if (width < 1 || width > 1000 || height < 1 || height > 1000) { if ( width < 1 || width > 1000 || height < 1 || height > 1000 ) {
fprintf(stderr, "bogus window size\n"); fprintf( stderr, "bogus window size\n" );
exit(1); exit( 1 );
} }
} }
Framebuffer local_framebuffers[ 2 ] = { Framebuffer(width,height), Framebuffer(width,height) }; Framebuffer local_framebuffers[2] = { Framebuffer( width, height ), Framebuffer( width, height ) };
Framebuffer *local_framebuffer = &(local_framebuffers[ fbmod ]); Framebuffer* local_framebuffer = &( local_framebuffers[fbmod] );
Framebuffer *new_state = &(local_framebuffers[ !fbmod ]); Framebuffer* new_state = &( local_framebuffers[!fbmod] );
Overlay::OverlayManager overlays; Overlay::OverlayManager overlays;
Display display( true ); Display display( true );
Complete local_terminal( width, height ); Complete local_terminal( width, height );
@@ -107,20 +107,18 @@ int main( int argc, char **argv )
overlays.apply( *new_state ); overlays.apply( *new_state );
/* calculate minimal difference from where we are */ /* calculate minimal difference from where we are */
const std::string diff( display.new_frame( false, const std::string diff( display.new_frame( false, *local_framebuffer, *new_state ) );
*local_framebuffer,
*new_state ) );
/* make sure to use diff */ /* make sure to use diff */
if ( diff.size() > INT_MAX ) { if ( diff.size() > INT_MAX ) {
exit( 1 ); exit( 1 );
} }
fbmod = !fbmod; fbmod = !fbmod;
local_framebuffer = &(local_framebuffers[ fbmod ]); local_framebuffer = &( local_framebuffers[fbmod] );
new_state = &(local_framebuffers[ !fbmod ]); new_state = &( local_framebuffers[!fbmod] );
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Exception caught: %s\n", e.what() ); fprintf( stderr, "Exception caught: %s\n", e.what() );
return 1; return 1;
} }
+5 -6
View File
@@ -38,15 +38,15 @@
using namespace Crypto; using namespace Crypto;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc != 2 ) { if ( argc != 2 ) {
fprintf( stderr, "Usage: %s KEY\n", argv[ 0 ] ); fprintf( stderr, "Usage: %s KEY\n", argv[0] );
return 1; return 1;
} }
try { try {
Base64Key key( argv[ 1 ] ); Base64Key key( argv[1] );
Session session( key ); Session session( key );
/* Read input */ /* Read input */
@@ -57,10 +57,9 @@ int main( int argc, char *argv[] )
Message message = session.decrypt( input.str() ); Message message = session.decrypt( input.str() );
fprintf( stderr, "Nonce = %ld\n", fprintf( stderr, "Nonce = %ld\n", (long)message.nonce.val() );
(long)message.nonce.val() );
std::cout << message.text; std::cout << message.text;
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
exit( 1 ); exit( 1 );
} }
+4 -4
View File
@@ -38,17 +38,17 @@
using namespace Crypto; using namespace Crypto;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc != 2 ) { if ( argc != 2 ) {
fprintf( stderr, "Usage: %s NONCE\n", argv[ 0 ] ); fprintf( stderr, "Usage: %s NONCE\n", argv[0] );
return 1; return 1;
} }
try { try {
Base64Key key; Base64Key key;
Session session( key ); Session session( key );
Nonce nonce( myatoi( argv[ 1 ] ) ); Nonce nonce( myatoi( argv[1] ) );
/* Read input */ /* Read input */
std::ostringstream input; std::ostringstream input;
@@ -61,7 +61,7 @@ int main( int argc, char *argv[] )
std::cerr << "Key: " << key.printable_key() << std::endl; std::cerr << "Key: " << key.printable_key() << std::endl;
std::cout << ciphertext; std::cout << ciphertext;
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
exit( 1 ); exit( 1 );
} }
+58 -61
View File
@@ -35,38 +35,38 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include "src/network/networktransport-impl.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/network/networktransport-impl.h"
#include "src/util/select.h" #include "src/util/select.h"
using namespace Network; using namespace Network;
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
bool server = true; bool server = true;
char *key; char* key;
char *ip; char* ip;
char *port; char* port;
UserStream me, remote; UserStream me, remote;
using NetworkPointer = std::shared_ptr<Transport<UserStream, UserStream>>; using NetworkPointer = std::shared_ptr<Transport<UserStream, UserStream>>;
Transport<UserStream, UserStream> *raw_n; Transport<UserStream, UserStream>* raw_n;
try { try {
if ( argc > 1 ) { if ( argc > 1 ) {
server = false; server = false;
/* client */ /* client */
key = argv[ 1 ]; key = argv[1];
ip = argv[ 2 ]; ip = argv[2];
port = argv[ 3 ]; port = argv[3];
raw_n = new Transport<UserStream, UserStream>( me, remote, key, ip, port ); raw_n = new Transport<UserStream, UserStream>( me, remote, key, ip, port );
} else { } else {
raw_n = new Transport<UserStream, UserStream>( me, remote, NULL, NULL ); raw_n = new Transport<UserStream, UserStream>( me, remote, NULL, NULL );
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Fatal startup error: %s\n", e.what() ); fprintf( stderr, "Fatal startup error: %s\n", e.what() );
exit( 1 ); exit( 1 );
} }
@@ -74,32 +74,33 @@ int main( int argc, char *argv[] )
fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() ); fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() );
if ( server ) { if ( server ) {
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
uint64_t last_num = n->get_remote_state_num(); uint64_t last_num = n->get_remote_state_num();
while ( true ) { while ( true ) {
try { try {
sel.clear_fds(); sel.clear_fds();
std::vector< int > fd_list( n->fds() ); std::vector<int> fd_list( n->fds() );
assert( fd_list.size() == 1 ); /* servers don't hop */ assert( fd_list.size() == 1 ); /* servers don't hop */
int network_fd = fd_list.back(); int network_fd = fd_list.back();
sel.add_fd( network_fd ); sel.add_fd( network_fd );
if ( sel.select( n->wait_time() ) < 0 ) { if ( sel.select( n->wait_time() ) < 0 ) {
perror( "select" ); perror( "select" );
exit( 1 ); exit( 1 );
} }
n->tick(); n->tick();
if ( sel.read( network_fd ) ) { if ( sel.read( network_fd ) ) {
n->recv(); n->recv();
if ( n->get_remote_state_num() != last_num ) { if ( n->get_remote_state_num() != last_num ) {
fprintf( stderr, "[%d=>%d %s]", (int)last_num, (int)n->get_remote_state_num(), n->get_remote_diff().c_str() ); fprintf(
last_num = n->get_remote_state_num(); stderr, "[%d=>%d %s]", (int)last_num, (int)n->get_remote_state_num(), n->get_remote_diff().c_str() );
} last_num = n->get_remote_state_num();
} }
} catch ( const std::exception &e ) { }
fprintf( stderr, "Server error: %s\n", e.what() ); } catch ( const std::exception& e ) {
fprintf( stderr, "Server error: %s\n", e.what() );
} }
} }
} else { } else {
@@ -120,48 +121,44 @@ int main( int argc, char *argv[] )
exit( 1 ); exit( 1 );
} }
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
while( true ) { while ( true ) {
sel.clear_fds(); sel.clear_fds();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
std::vector< int > fd_list( n->fds() ); std::vector<int> fd_list( n->fds() );
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end(); sel.add_fd( *it );
it++ ) {
sel.add_fd( *it );
} }
try { try {
if ( sel.select( n->wait_time() ) < 0 ) { if ( sel.select( n->wait_time() ) < 0 ) {
perror( "select" ); perror( "select" );
} }
n->tick(); n->tick();
if ( sel.read( STDIN_FILENO ) ) { if ( sel.read( STDIN_FILENO ) ) {
char x; char x;
fatal_assert( read( STDIN_FILENO, &x, 1 ) == 1 ); fatal_assert( read( STDIN_FILENO, &x, 1 ) == 1 );
n->get_current_state().push_back( Parser::UserByte( x ) ); n->get_current_state().push_back( Parser::UserByte( x ) );
} }
bool network_ready_to_read = false; bool network_ready_to_read = false;
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end(); if ( sel.read( *it ) ) {
it++ ) { /* packet received from the network */
if ( sel.read( *it ) ) { /* we only read one socket each run */
/* packet received from the network */ network_ready_to_read = true;
/* we only read one socket each run */ }
network_ready_to_read = true; }
}
}
if ( network_ready_to_read ) { if ( network_ready_to_read ) {
n->recv(); n->recv();
} }
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Client error: %s\n", e.what() ); fprintf( stderr, "Client error: %s\n", e.what() );
} }
} }
+27 -29
View File
@@ -55,21 +55,19 @@
#endif #endif
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
#include "src/util/swrite.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
const size_t buf_size = 1024; const size_t buf_size = 1024;
static void emulate_terminal( int fd ); static void emulate_terminal( int fd );
static int copy( int src, int dest ); static int copy( int src, int dest );
static int vt_parser( int fd, Parser::UTF8Parser *parser ); static int vt_parser( int fd, Parser::UTF8Parser* parser );
int main( int argc __attribute__((unused)), int main( int argc __attribute__( ( unused ) ), char* argv[] __attribute__( ( unused ) ), char* envp[] )
char *argv[] __attribute__((unused)),
char *envp[] )
{ {
int master; int master;
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
@@ -85,12 +83,14 @@ int main( int argc __attribute__((unused)),
child_termios = saved_termios; child_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(child_termios.c_iflag & IUTF8) ) { if ( !( child_termios.c_iflag & IUTF8 ) ) {
fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
child_termios.c_iflag |= IUTF8; child_termios.c_iflag |= IUTF8;
} }
#else #else
fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); fprintf( stderr,
"Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does "
"not work properly on this platform.\n" );
#endif /* HAVE_IUTF8 */ #endif /* HAVE_IUTF8 */
pid_t child = forkpty( &master, NULL, &child_termios, NULL ); pid_t child = forkpty( &master, NULL, &child_termios, NULL );
@@ -102,11 +102,11 @@ int main( int argc __attribute__((unused)),
if ( child == 0 ) { if ( child == 0 ) {
/* child */ /* child */
char *my_argv[ 2 ]; char* my_argv[2];
my_argv[ 0 ] = strdup( "/bin/bash" ); my_argv[0] = strdup( "/bin/bash" );
assert( my_argv[ 0 ] ); assert( my_argv[0] );
my_argv[ 1 ] = NULL; my_argv[1] = NULL;
if ( execve( "/bin/bash", my_argv, envp ) < 0 ) { if ( execve( "/bin/bash", my_argv, envp ) < 0 ) {
perror( "execve" ); perror( "execve" );
@@ -139,7 +139,7 @@ static void emulate_terminal( int fd )
{ {
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
sel.add_fd( fd ); sel.add_fd( fd );
@@ -152,11 +152,11 @@ static void emulate_terminal( int fd )
if ( sel.read( STDIN_FILENO ) ) { if ( sel.read( STDIN_FILENO ) ) {
if ( copy( STDIN_FILENO, fd ) < 0 ) { if ( copy( STDIN_FILENO, fd ) < 0 ) {
return; return;
} }
} else if ( sel.read( fd ) ) { } else if ( sel.read( fd ) ) {
if ( vt_parser( fd, &parser ) < 0 ) { if ( vt_parser( fd, &parser ) < 0 ) {
return; return;
} }
} else { } else {
fprintf( stderr, "select mysteriously woken up\n" ); fprintf( stderr, "select mysteriously woken up\n" );
@@ -166,7 +166,7 @@ static void emulate_terminal( int fd )
static int copy( int src, int dest ) static int copy( int src, int dest )
{ {
char buf[ buf_size ]; char buf[buf_size];
ssize_t bytes_read = read( src, buf, buf_size ); ssize_t bytes_read = read( src, buf, buf_size );
if ( bytes_read == 0 ) { /* EOF */ if ( bytes_read == 0 ) { /* EOF */
@@ -179,9 +179,9 @@ static int copy( int src, int dest )
return swrite( dest, buf, bytes_read ); return swrite( dest, buf, bytes_read );
} }
static int vt_parser( int fd, Parser::UTF8Parser *parser ) static int vt_parser( int fd, Parser::UTF8Parser* parser )
{ {
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
@@ -195,22 +195,20 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser )
/* feed to parser */ /* feed to parser */
Parser::Actions actions; Parser::Actions actions;
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
parser->input( buf[ i ], actions ); parser->input( buf[i], actions );
for ( Parser::Actions::iterator j = actions.begin(); for ( Parser::Actions::iterator j = actions.begin(); j != actions.end(); j++ ) {
j != actions.end();
j++ ) {
assert( *j ); assert( *j );
Parser::Action &act = **j; Parser::Action& act = **j;
if ( act.char_present ) { if ( act.char_present ) {
if ( iswprint( act.ch ) ) { if ( iswprint( act.ch ) ) {
printf( "%s(0x%02x=%lc) ", act.name().c_str(), (unsigned int)act.ch, (wint_t)act.ch ); printf( "%s(0x%02x=%lc) ", act.name().c_str(), (unsigned int)act.ch, (wint_t)act.ch );
} else { } else {
printf( "%s(0x%02x) ", act.name().c_str(), (unsigned int)act.ch ); printf( "%s(0x%02x) ", act.name().c_str(), (unsigned int)act.ch );
} }
} else { } else {
printf( "[%s] ", act.name().c_str() ); printf( "[%s] ", act.name().c_str() );
} }
fflush( stdout ); fflush( stdout );
+40 -41
View File
@@ -61,19 +61,19 @@
#include <libutil.h> #include <libutil.h>
#endif #endif
#include "src/terminal/parser.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/util/swrite.h" #include "src/terminal/parser.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/pty_compat.h"
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
#include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
const size_t buf_size = 16384; const size_t buf_size = 16384;
static void emulate_terminal( int fd ); static void emulate_terminal( int fd );
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
int master; int master;
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
@@ -89,12 +89,14 @@ int main( int argc, char *argv[] )
child_termios = saved_termios; child_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(child_termios.c_iflag & IUTF8) ) { if ( !( child_termios.c_iflag & IUTF8 ) ) {
fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
child_termios.c_iflag |= IUTF8; child_termios.c_iflag |= IUTF8;
} }
#else #else
fprintf( stderr, "Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does not work properly on this platform.\n" ); fprintf( stderr,
"Warning: termios IUTF8 flag not defined. Character-erase of multibyte character sequence probably does "
"not work properly on this platform.\n" );
#endif /* HAVE_IUTF8 */ #endif /* HAVE_IUTF8 */
pid_t child = forkpty( &master, NULL, &child_termios, NULL ); pid_t child = forkpty( &master, NULL, &child_termios, NULL );
@@ -117,26 +119,26 @@ int main( int argc, char *argv[] )
exit( 1 ); exit( 1 );
} }
char *my_argv[ 2 ]; char* my_argv[2];
if ( argc > 1 ) { if ( argc > 1 ) {
argv++; argv++;
} else { } else {
/* get shell name */ /* get shell name */
my_argv[ 0 ] = getenv( "SHELL" ); my_argv[0] = getenv( "SHELL" );
if ( my_argv[ 0 ] == NULL || *my_argv[ 0 ] == '\0' ) { if ( my_argv[0] == NULL || *my_argv[0] == '\0' ) {
struct passwd *pw = getpwuid( getuid() ); struct passwd* pw = getpwuid( getuid() );
if ( pw == NULL ) { if ( pw == NULL ) {
perror( "getpwuid" ); perror( "getpwuid" );
exit( 1 ); exit( 1 );
} }
my_argv[ 0 ] = strdup( pw->pw_shell ); my_argv[0] = strdup( pw->pw_shell );
} }
assert( my_argv[ 0 ] ); assert( my_argv[0] );
my_argv[ 1 ] = NULL; my_argv[1] = NULL;
argv = my_argv; argv = my_argv;
} }
if ( execvp( argv[ 0 ], argv ) < 0 ) { if ( execvp( argv[0], argv ) < 0 ) {
perror( "execve" ); perror( "execve" );
exit( 1 ); exit( 1 );
} }
@@ -154,7 +156,7 @@ int main( int argc, char *argv[] )
try { try {
emulate_terminal( master ); emulate_terminal( master );
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "\r\nException caught: %s\r\n", e.what() ); fprintf( stderr, "\r\nException caught: %s\r\n", e.what() );
} }
@@ -170,8 +172,7 @@ int main( int argc, char *argv[] )
} }
/* Print a frame if the last frame was more than 1/50 seconds ago */ /* Print a frame if the last frame was more than 1/50 seconds ago */
static bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame, static bool tick( Terminal::Framebuffer& state, Terminal::Framebuffer& new_frame, const Terminal::Display& display )
const Terminal::Display &display )
{ {
static bool initialized = false; static bool initialized = false;
static struct timeval last_time; static struct timeval last_time;
@@ -182,11 +183,9 @@ static bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame
perror( "gettimeofday" ); perror( "gettimeofday" );
} }
double diff = (this_time.tv_sec - last_time.tv_sec) double diff = ( this_time.tv_sec - last_time.tv_sec ) + .000001 * ( this_time.tv_usec - last_time.tv_usec );
+ .000001 * (this_time.tv_usec - last_time.tv_usec);
if ( (!initialized) if ( ( !initialized ) || ( diff >= 0.02 ) ) {
|| (diff >= 0.02) ) {
std::string update = display.new_frame( initialized, state, new_frame ); std::string update = display.new_frame( initialized, state, new_frame );
swrite( STDOUT_FILENO, update.c_str() ); swrite( STDOUT_FILENO, update.c_str() );
state = new_frame; state = new_frame;
@@ -239,7 +238,7 @@ static void emulate_terminal( int fd )
/* open display */ /* open display */
Terminal::Display display( true ); /* use TERM to initialize */ Terminal::Display display( true ); /* use TERM to initialize */
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
sel.add_fd( fd ); sel.add_fd( fd );
sel.add_signal( SIGWINCH ); sel.add_signal( SIGWINCH );
@@ -257,48 +256,48 @@ static void emulate_terminal( int fd )
if ( sel.read( STDIN_FILENO ) ) { if ( sel.read( STDIN_FILENO ) ) {
/* input from user */ /* input from user */
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size ); ssize_t bytes_read = read( STDIN_FILENO, buf, buf_size );
if ( bytes_read == 0 ) { /* EOF */ if ( bytes_read == 0 ) { /* EOF */
return; return;
} else if ( bytes_read < 0 ) { } else if ( bytes_read < 0 ) {
perror( "read" ); perror( "read" );
return; return;
} }
std::string terminal_to_host; std::string terminal_to_host;
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
terminal_to_host += complete.act( Parser::UserByte( buf[ i ] ) ); terminal_to_host += complete.act( Parser::UserByte( buf[i] ) );
} }
if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) {
break; break;
} }
} else if ( sel.read( fd ) ) { } else if ( sel.read( fd ) ) {
/* input from host */ /* input from host */
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
if ( bytes_read == 0 ) { /* EOF */ if ( bytes_read == 0 ) { /* EOF */
return; return;
} else if ( bytes_read < 0 ) { } else if ( bytes_read < 0 ) {
perror( "read" ); perror( "read" );
return; return;
} }
std::string terminal_to_host = complete.act( std::string( buf, bytes_read ) ); std::string terminal_to_host = complete.act( std::string( buf, bytes_read ) );
if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { if ( swrite( fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) {
break; break;
} }
} else if ( sel.signal( SIGWINCH ) ) { } else if ( sel.signal( SIGWINCH ) ) {
/* get new size */ /* get new size */
if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) {
perror( "ioctl TIOCGWINSZ" ); perror( "ioctl TIOCGWINSZ" );
return; return;
} }
/* tell emulator */ /* tell emulator */
@@ -306,8 +305,8 @@ static void emulate_terminal( int fd )
/* tell child process */ /* tell child process */
if ( ioctl( fd, TIOCSWINSZ, &window_size ) < 0 ) { if ( ioctl( fd, TIOCSWINSZ, &window_size ) < 0 ) {
perror( "ioctl TIOCSWINSZ" ); perror( "ioctl TIOCSWINSZ" );
return; return;
} }
} }
+61 -61
View File
@@ -37,10 +37,10 @@
#include <unistd.h> #include <unistd.h>
#include "stmclient.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/util/locale_utils.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h"
#include "stmclient.h"
/* These need to be included last because of conflicting defines. */ /* These need to be included last because of conflicting defines. */
/* /*
@@ -53,59 +53,62 @@
#endif #endif
#if defined HAVE_NCURSESW_CURSES_H #if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h> #include <ncursesw/curses.h>
# include <ncursesw/term.h> #include <ncursesw/term.h>
#elif defined HAVE_NCURSESW_H #elif defined HAVE_NCURSESW_H
# include <ncursesw.h> #include <ncursesw.h>
# include <term.h> #include <term.h>
#elif defined HAVE_NCURSES_CURSES_H #elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h> #include <ncurses/curses.h>
# include <ncurses/term.h> #include <ncurses/term.h>
#elif defined HAVE_NCURSES_H #elif defined HAVE_NCURSES_H
# include <ncurses.h> #include <ncurses.h>
# include <term.h> #include <term.h>
#elif defined HAVE_CURSES_H #elif defined HAVE_CURSES_H
# include <curses.h> #include <curses.h>
# include <term.h> #include <term.h>
#else #else
# error "SysV or X/Open-compatible Curses header file required" #error "SysV or X/Open-compatible Curses header file required"
#endif #endif
static void print_version( FILE *file ) static void print_version( FILE* file )
{ {
fputs( "mosh-client (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n" fputs( "mosh-client (" PACKAGE_STRING ") [build " BUILD_VERSION "]\n"
"Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n" "Copyright 2012 Keith Winstein <mosh-devel@mit.edu>\n"
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n"
"This is free software: you are free to change and redistribute it.\n" "This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n", file ); "There is NO WARRANTY, to the extent permitted by law.\n",
file );
} }
static void print_usage( FILE *file, const char *argv0 ) static void print_usage( FILE* file, const char* argv0 )
{ {
print_version( file ); print_version( file );
fprintf( file, "\nUsage: %s [-# 'ARGS'] IP PORT\n" fprintf( file,
" %s -c\n", argv0, argv0 ); "\nUsage: %s [-# 'ARGS'] IP PORT\n"
" %s -c\n",
argv0,
argv0 );
} }
static void print_colorcount( void ) static void print_colorcount( void )
{ {
/* check colors */ /* check colors */
setupterm((char *)0, 1, (int *)0); setupterm( (char*)0, 1, (int*)0 );
char colors_name[] = "colors"; char colors_name[] = "colors";
int color_val = tigetnum( colors_name ); int color_val = tigetnum( colors_name );
if ( color_val == -2 ) { if ( color_val == -2 ) {
fprintf( stderr, "Invalid terminfo numeric capability: %s\n", fprintf( stderr, "Invalid terminfo numeric capability: %s\n", colors_name );
colors_name );
} }
printf( "%d\n", color_val ); printf( "%d\n", color_val );
} }
#ifdef NACL #ifdef NACL
int mosh_main( int argc, char *argv[] ) int mosh_main( int argc, char* argv[] )
#else #else
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
#endif #endif
{ {
unsigned int verbose = 0; unsigned int verbose = 0;
@@ -117,67 +120,66 @@ int main( int argc, char *argv[] )
/* Get arguments */ /* Get arguments */
for ( int i = 1; i < argc; i++ ) { for ( int i = 1; i < argc; i++ ) {
if ( 0 == strcmp( argv[ i ], "--help" ) ) { if ( 0 == strcmp( argv[i], "--help" ) ) {
print_usage( stdout, argv[ 0 ] ); print_usage( stdout, argv[0] );
exit( 0 ); exit( 0 );
} }
if ( 0 == strcmp( argv[ i ], "--version" ) ) { if ( 0 == strcmp( argv[i], "--version" ) ) {
print_version( stdout ); print_version( stdout );
exit( 0 ); exit( 0 );
} }
} }
int opt; int opt;
while ( (opt = getopt( argc, argv, "#:cv" )) != -1 ) { while ( ( opt = getopt( argc, argv, "#:cv" ) ) != -1 ) {
switch ( opt ) { switch ( opt ) {
case '#': case '#':
// Ignore the original arguments to mosh wrapper // Ignore the original arguments to mosh wrapper
break; break;
case 'c': case 'c':
print_colorcount(); print_colorcount();
exit( 0 ); exit( 0 );
break; break;
case 'v': case 'v':
verbose++; verbose++;
break; break;
default: default:
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
break; break;
} }
} }
char *ip, *desired_port; char *ip, *desired_port;
if ( argc - optind != 2 ) { if ( argc - optind != 2 ) {
print_usage( stderr, argv[ 0 ] ); print_usage( stderr, argv[0] );
exit( 1 ); exit( 1 );
} }
ip = argv[ optind ]; ip = argv[optind];
desired_port = argv[ optind + 1 ]; desired_port = argv[optind + 1];
/* Sanity-check arguments */ /* Sanity-check arguments */
if ( desired_port if ( desired_port && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) {
&& ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) { fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[0], desired_port );
fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[ 0 ], desired_port ); print_usage( stderr, argv[0] );
print_usage( stderr, argv[ 0 ] );
exit( 1 ); exit( 1 );
} }
/* Read key from environment */ /* Read key from environment */
char *env_key = getenv( "MOSH_KEY" ); char* env_key = getenv( "MOSH_KEY" );
if ( env_key == NULL ) { if ( env_key == NULL ) {
fputs( "MOSH_KEY environment variable not found.\n", stderr ); fputs( "MOSH_KEY environment variable not found.\n", stderr );
exit( 1 ); exit( 1 );
} }
/* Read prediction preference */ /* Read prediction preference */
char *predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" ); char* predict_mode = getenv( "MOSH_PREDICTION_DISPLAY" );
/* can be NULL */ /* can be NULL */
/* Read prediction insertion preference */ /* Read prediction insertion preference */
char *predict_overwrite = getenv( "MOSH_PREDICTION_OVERWRITE" ); char* predict_overwrite = getenv( "MOSH_PREDICTION_OVERWRITE" );
/* can be NULL */ /* can be NULL */
std::string key( env_key ); std::string key( env_key );
@@ -203,15 +205,13 @@ int main( int argc, char *argv[] )
} }
client.shutdown(); client.shutdown();
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
fprintf( stderr, "Network exception: %s\r\n", fprintf( stderr, "Network exception: %s\r\n", e.what() );
e.what() );
success = false; success = false;
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
fprintf( stderr, "Crypto exception: %s\r\n", fprintf( stderr, "Crypto exception: %s\r\n", e.what() );
e.what() );
success = false; success = false;
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
success = false; success = false;
} }
File diff suppressed because it is too large Load Diff
+143 -139
View File
@@ -52,15 +52,15 @@
#include <util.h> #include <util.h>
#endif #endif
#include "stmclient.h"
#include "src/util/swrite.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/select.h" #include "src/util/select.h"
#include "src/util/swrite.h"
#include "src/util/timestamp.h" #include "src/util/timestamp.h"
#include "stmclient.h"
#include "src/network/networktransport-impl.h" #include "src/network/networktransport-impl.h"
@@ -68,8 +68,8 @@ void STMClient::resume( void )
{ {
/* Restore termios state */ /* Restore termios state */
if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) {
perror( "tcsetattr" ); perror( "tcsetattr" );
exit( 1 ); exit( 1 );
} }
/* Put terminal in application-cursor-key mode */ /* Put terminal in application-cursor-key mode */
@@ -85,11 +85,13 @@ void STMClient::init( void )
LocaleVar native_ctype = get_ctype(); LocaleVar native_ctype = get_ctype();
std::string native_charset( locale_charset() ); std::string native_charset( locale_charset() );
fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n" fprintf( stderr,
"Unfortunately, the client's environment (%s) specifies\n" "mosh-client needs a UTF-8 native locale to run.\n\n"
"the character set \"%s\".\n\n", "Unfortunately, the client's environment (%s) specifies\n"
native_ctype.str().c_str(), native_charset.c_str() ); "the character set \"%s\".\n\n",
int unused __attribute((unused)) = system( "locale" ); native_ctype.str().c_str(),
native_charset.c_str() );
int unused __attribute( ( unused ) ) = system( "locale" );
exit( 1 ); exit( 1 );
} }
@@ -103,7 +105,7 @@ void STMClient::init( void )
raw_termios = saved_termios; raw_termios = saved_termios;
#ifdef HAVE_IUTF8 #ifdef HAVE_IUTF8
if ( !(raw_termios.c_iflag & IUTF8) ) { if ( !( raw_termios.c_iflag & IUTF8 ) ) {
// fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" ); // fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
/* Probably not really necessary since we are putting terminal driver into raw mode anyway. */ /* Probably not really necessary since we are putting terminal driver into raw mode anyway. */
raw_termios.c_iflag |= IUTF8; raw_termios.c_iflag |= IUTF8;
@@ -113,8 +115,8 @@ void STMClient::init( void )
cfmakeraw( &raw_termios ); cfmakeraw( &raw_termios );
if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) { if ( tcsetattr( STDIN_FILENO, TCSANOW, &raw_termios ) < 0 ) {
perror( "tcsetattr" ); perror( "tcsetattr" );
exit( 1 ); exit( 1 );
} }
/* Put terminal in application-cursor-key mode */ /* Put terminal in application-cursor-key mode */
@@ -126,29 +128,29 @@ void STMClient::init( void )
} }
/* Set terminal escape key. */ /* Set terminal escape key. */
const char *escape_key_env; const char* escape_key_env;
if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) { if ( ( escape_key_env = getenv( "MOSH_ESCAPE_KEY" ) ) != NULL ) {
if ( strlen( escape_key_env ) == 1 ) { if ( strlen( escape_key_env ) == 1 ) {
escape_key = (int)escape_key_env[0]; escape_key = (int)escape_key_env[0];
if ( escape_key > 0 && escape_key < 128 ) { if ( escape_key > 0 && escape_key < 128 ) {
if ( escape_key < 32 ) { if ( escape_key < 32 ) {
/* If escape is ctrl-something, pass it with repeating the key without ctrl. */ /* If escape is ctrl-something, pass it with repeating the key without ctrl. */
escape_pass_key = escape_key + (int)'@'; escape_pass_key = escape_key + (int)'@';
} else { } else {
/* If escape is something else, pass it with repeating the key itself. */ /* If escape is something else, pass it with repeating the key itself. */
escape_pass_key = escape_key; escape_pass_key = escape_key;
} }
if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) { if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) {
/* If escape pass is an upper case character, define optional version /* If escape pass is an upper case character, define optional version
as lower case of the same. */ as lower case of the same. */
escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A'; escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A';
} else { } else {
escape_pass_key2 = escape_pass_key; escape_pass_key2 = escape_pass_key;
} }
} else { } else {
escape_key = 0x1E; escape_key = 0x1E;
escape_pass_key = '^'; escape_pass_key = '^';
escape_pass_key2 = '^'; escape_pass_key2 = '^';
} }
} else if ( strlen( escape_key_env ) == 0 ) { } else if ( strlen( escape_key_env ) == 0 ) {
escape_key = -1; escape_key = -1;
@@ -166,7 +168,8 @@ void STMClient::init( void )
/* There are so many better ways to shoot oneself into leg than /* There are so many better ways to shoot oneself into leg than
setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn
that we just won't allow that. */ that we just won't allow that. */
if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) { if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C
|| escape_key == 0x0D ) {
escape_key = 0x1E; escape_key = 0x1E;
escape_pass_key = '^'; escape_pass_key = '^';
escape_pass_key2 = '^'; escape_pass_key2 = '^';
@@ -176,23 +179,24 @@ void STMClient::init( void )
if ( escape_key > 0 ) { if ( escape_key > 0 ) {
char escape_pass_name_buf[16]; char escape_pass_name_buf[16];
char escape_key_name_buf[16]; char escape_key_name_buf[16];
snprintf(escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key); snprintf( escape_pass_name_buf, sizeof escape_pass_name_buf, "\"%c\"", escape_pass_key );
if (escape_key < 32) { if ( escape_key < 32 ) {
snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key); snprintf( escape_key_name_buf, sizeof escape_key_name_buf, "Ctrl-%c", escape_pass_key );
escape_requires_lf = false; escape_requires_lf = false;
} else { } else {
snprintf(escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key); snprintf( escape_key_name_buf, sizeof escape_key_name_buf, "\"%c\"", escape_key );
escape_requires_lf = true; escape_requires_lf = true;
} }
std::string tmp; std::string tmp;
tmp = std::string( escape_pass_name_buf ); tmp = std::string( escape_pass_name_buf );
std::wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end()); std::wstring escape_pass_name = std::wstring( tmp.begin(), tmp.end() );
tmp = std::string( escape_key_name_buf ); tmp = std::string( escape_key_name_buf );
std::wstring escape_key_name = std::wstring(tmp.begin(), tmp.end()); std::wstring escape_key_name = std::wstring( tmp.begin(), tmp.end() );
escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name; escape_key_help
= L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name;
overlays.get_notification_engine().set_escape_key_string( tmp ); overlays.get_notification_engine().set_escape_key_string( tmp );
} }
wchar_t tmp[ 128 ]; wchar_t tmp[128];
swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() ); swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() );
connecting_notification = std::wstring( tmp ); connecting_notification = std::wstring( tmp );
} }
@@ -214,19 +218,24 @@ void STMClient::shutdown( void )
} }
if ( still_connecting() ) { if ( still_connecting() ) {
fprintf( stderr, "\nmosh did not make a successful connection to %s:%s.\n" fprintf( stderr,
"Please verify that UDP port %s is not firewalled and can reach the server.\n\n" "\nmosh did not make a successful connection to %s:%s.\n"
"(By default, mosh uses a UDP port between 60000 and 61000. The -p option\n" "Please verify that UDP port %s is not firewalled and can reach the server.\n\n"
"selects a specific UDP port number.)\n", ip.c_str(), port.c_str(), port.c_str() ); "(By default, mosh uses a UDP port between 60000 and 61000. The -p option\n"
"selects a specific UDP port number.)\n",
ip.c_str(),
port.c_str(),
port.c_str() );
} else if ( network && !clean_shutdown ) { } else if ( network && !clean_shutdown ) {
fputs( "\n\nmosh did not shut down cleanly. Please note that the\n" fputs( "\n\nmosh did not shut down cleanly. Please note that the\n"
"mosh-server process may still be running on the server.\n", stderr ); "mosh-server process may still be running on the server.\n",
stderr );
} }
} }
void STMClient::main_init( void ) void STMClient::main_init( void )
{ {
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
sel.add_signal( SIGWINCH ); sel.add_signal( SIGWINCH );
sel.add_signal( SIGTERM ); sel.add_signal( SIGTERM );
sel.add_signal( SIGINT ); sel.add_signal( SIGINT );
@@ -276,9 +285,7 @@ void STMClient::output_new_frame( void )
overlays.apply( new_state ); overlays.apply( new_state );
/* calculate minimal difference from where we are */ /* calculate minimal difference from where we are */
const std::string diff( display.new_frame( !repaint_requested, const std::string diff( display.new_frame( !repaint_requested, local_framebuffer, new_state ) );
local_framebuffer,
new_state ) );
swrite( STDOUT_FILENO, diff.data(), diff.size() ); swrite( STDOUT_FILENO, diff.data(), diff.size() );
repaint_requested = false; repaint_requested = false;
@@ -296,13 +303,14 @@ void STMClient::process_network_input( void )
overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() ); overlays.get_prediction_engine().set_local_frame_acked( network->get_sent_state_acked() );
overlays.get_prediction_engine().set_send_interval( network->send_interval() ); overlays.get_prediction_engine().set_send_interval( network->send_interval() );
overlays.get_prediction_engine().set_local_frame_late_acked( network->get_latest_remote_state().state.get_echo_ack() ); overlays.get_prediction_engine().set_local_frame_late_acked(
network->get_latest_remote_state().state.get_echo_ack() );
} }
bool STMClient::process_user_input( int fd ) bool STMClient::process_user_input( int fd )
{ {
const int buf_size = 16384; const int buf_size = 16384;
char buf[ buf_size ]; char buf[buf_size];
/* fill buffer if possible */ /* fill buffer if possible */
ssize_t bytes_read = read( fd, buf, buf_size ); ssize_t bytes_read = read( fd, buf, buf_size );
@@ -313,7 +321,7 @@ bool STMClient::process_user_input( int fd )
return false; return false;
} }
NetworkType &net = *network; NetworkType& net = *network;
if ( net.shutdown_in_progress() ) { if ( net.shutdown_in_progress() ) {
return true; return true;
@@ -327,7 +335,7 @@ bool STMClient::process_user_input( int fd )
} }
for ( int i = 0; i < bytes_read; i++ ) { for ( int i = 0; i < bytes_read; i++ ) {
char the_byte = buf[ i ]; char the_byte = buf[i];
if ( !paste ) { if ( !paste ) {
overlays.get_prediction_engine().new_user_byte( the_byte, local_framebuffer ); overlays.get_prediction_engine().new_user_byte( the_byte, local_framebuffer );
@@ -335,56 +343,59 @@ bool STMClient::process_user_input( int fd )
if ( quit_sequence_started ) { if ( quit_sequence_started ) {
if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */
if ( net.has_remote_addr() && (!net.shutdown_in_progress()) ) { if ( net.has_remote_addr() && ( !net.shutdown_in_progress() ) ) {
overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting on user request..." ), true ); overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting on user request..." ),
net.start_shutdown(); true );
return true; net.start_shutdown();
} return true;
return false; }
return false;
} else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */ } else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */
/* Restore terminal and terminal-driver state */ /* Restore terminal and terminal-driver state */
swrite( STDOUT_FILENO, display.close().c_str() ); swrite( STDOUT_FILENO, display.close().c_str() );
if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) { if ( tcsetattr( STDIN_FILENO, TCSANOW, &saved_termios ) < 0 ) {
perror( "tcsetattr" ); perror( "tcsetattr" );
exit( 1 ); exit( 1 );
} }
fputs( "\n\033[37;44m[mosh is suspended.]\033[m\n", stdout ); fputs( "\n\033[37;44m[mosh is suspended.]\033[m\n", stdout );
fflush( NULL ); fflush( NULL );
/* actually suspend */ /* actually suspend */
kill( 0, SIGSTOP ); kill( 0, SIGSTOP );
resume(); resume();
} else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) { } else if ( ( the_byte == escape_pass_key ) || ( the_byte == escape_pass_key2 ) ) {
/* Emulation sequence to type escape_key is escape_key + /* Emulation sequence to type escape_key is escape_key +
escape_pass_key (that is escape key without Ctrl) */ escape_pass_key (that is escape key without Ctrl) */
net.get_current_state().push_back( Parser::UserByte( escape_key ) ); net.get_current_state().push_back( Parser::UserByte( escape_key ) );
} else { } else {
/* Escape key followed by anything other than . and ^ gets sent literally */ /* Escape key followed by anything other than . and ^ gets sent literally */
net.get_current_state().push_back( Parser::UserByte( escape_key ) ); net.get_current_state().push_back( Parser::UserByte( escape_key ) );
net.get_current_state().push_back( Parser::UserByte( the_byte ) ); net.get_current_state().push_back( Parser::UserByte( the_byte ) );
} }
quit_sequence_started = false; quit_sequence_started = false;
if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) { if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) {
overlays.get_notification_engine().set_notification_string( L"" ); overlays.get_notification_engine().set_notification_string( L"" );
} }
continue; continue;
} }
quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf)); quit_sequence_started
= ( escape_key > 0 ) && ( the_byte == escape_key ) && ( lf_entered || ( !escape_requires_lf ) );
if ( quit_sequence_started ) { if ( quit_sequence_started ) {
lf_entered = false; lf_entered = false;
overlays.get_notification_engine().set_notification_string( escape_key_help, true, false ); overlays.get_notification_engine().set_notification_string( escape_key_help, true, false );
continue; continue;
} }
lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */ lf_entered = ( ( the_byte == 0x0A )
|| ( the_byte == 0x0D ) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */
if ( the_byte == 0x0C ) { /* Ctrl-L */ if ( the_byte == 0x0C ) { /* Ctrl-L */
repaint_requested = true; repaint_requested = true;
@@ -427,14 +438,14 @@ bool STMClient::main( void )
/* Drop unnecessary privileges */ /* Drop unnecessary privileges */
#ifdef HAVE_PLEDGE #ifdef HAVE_PLEDGE
/* OpenBSD pledge() syscall */ /* OpenBSD pledge() syscall */
if ( pledge( "stdio inet tty", NULL )) { if ( pledge( "stdio inet tty", NULL ) ) {
perror( "pledge() failed" ); perror( "pledge() failed" );
exit( 1 ); exit( 1 );
} }
#endif #endif
/* prepare to poll for events */ /* prepare to poll for events */
Select &sel = Select::get_instance(); Select& sel = Select::get_instance();
while ( 1 ) { while ( 1 ) {
try { try {
@@ -444,117 +455,111 @@ bool STMClient::main( void )
/* Handle startup "Connecting..." message */ /* Handle startup "Connecting..." message */
if ( still_connecting() ) { if ( still_connecting() ) {
wait_time = std::min( 250, wait_time ); wait_time = std::min( 250, wait_time );
} }
/* poll for events */ /* poll for events */
/* network->fd() can in theory change over time */ /* network->fd() can in theory change over time */
sel.clear_fds(); sel.clear_fds();
std::vector< int > fd_list( network->fds() ); std::vector<int> fd_list( network->fds() );
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end(); sel.add_fd( *it );
it++ ) {
sel.add_fd( *it );
} }
sel.add_fd( STDIN_FILENO ); sel.add_fd( STDIN_FILENO );
int active_fds = sel.select( wait_time ); int active_fds = sel.select( wait_time );
if ( active_fds < 0 ) { if ( active_fds < 0 ) {
perror( "select" ); perror( "select" );
break; break;
} }
bool network_ready_to_read = false; bool network_ready_to_read = false;
for ( std::vector< int >::const_iterator it = fd_list.begin(); for ( std::vector<int>::const_iterator it = fd_list.begin(); it != fd_list.end(); it++ ) {
it != fd_list.end(); if ( sel.read( *it ) ) {
it++ ) { /* packet received from the network */
if ( sel.read( *it ) ) { /* we only read one socket each run */
/* packet received from the network */ network_ready_to_read = true;
/* we only read one socket each run */ }
network_ready_to_read = true;
}
} }
if ( network_ready_to_read ) { if ( network_ready_to_read ) {
process_network_input(); process_network_input();
} }
if ( sel.read( STDIN_FILENO ) && !process_user_input( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */ if ( sel.read( STDIN_FILENO )
if ( !network->has_remote_addr() ) { && !process_user_input( STDIN_FILENO ) ) { /* input from the user needs to be fed to the network */
break; if ( !network->has_remote_addr() ) {
} else if ( !network->shutdown_in_progress() ) { break;
overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting..." ), true ); } else if ( !network->shutdown_in_progress() ) {
network->start_shutdown(); overlays.get_notification_engine().set_notification_string( std::wstring( L"Exiting..." ), true );
} network->start_shutdown();
}
} }
if ( sel.signal( SIGWINCH ) && !process_resize() ) { /* resize */ if ( sel.signal( SIGWINCH ) && !process_resize() ) { /* resize */
return false; return false;
} }
if ( sel.signal( SIGCONT ) ) { if ( sel.signal( SIGCONT ) ) {
resume(); resume();
} }
if ( sel.signal( SIGTERM ) if ( sel.signal( SIGTERM ) || sel.signal( SIGINT ) || sel.signal( SIGHUP ) || sel.signal( SIGPIPE ) ) {
|| sel.signal( SIGINT )
|| sel.signal( SIGHUP )
|| sel.signal( SIGPIPE ) ) {
/* shutdown signal */ /* shutdown signal */
if ( !network->has_remote_addr() ) { if ( !network->has_remote_addr() ) {
break; break;
} else if ( !network->shutdown_in_progress() ) { } else if ( !network->shutdown_in_progress() ) {
overlays.get_notification_engine().set_notification_string( std::wstring( L"Signal received, shutting down..." ), true ); overlays.get_notification_engine().set_notification_string(
std::wstring( L"Signal received, shutting down..." ), true );
network->start_shutdown(); network->start_shutdown();
} }
} }
/* quit if our shutdown has been acknowledged */ /* quit if our shutdown has been acknowledged */
if ( network->shutdown_in_progress() && network->shutdown_acknowledged() ) { if ( network->shutdown_in_progress() && network->shutdown_acknowledged() ) {
clean_shutdown = true; clean_shutdown = true;
break; break;
} }
/* quit after shutdown acknowledgement timeout */ /* quit after shutdown acknowledgement timeout */
if ( network->shutdown_in_progress() && network->shutdown_ack_timed_out() ) { if ( network->shutdown_in_progress() && network->shutdown_ack_timed_out() ) {
break; break;
} }
/* quit if we received and acknowledged a shutdown request */ /* quit if we received and acknowledged a shutdown request */
if ( network->counterparty_shutdown_ack_sent() ) { if ( network->counterparty_shutdown_ack_sent() ) {
clean_shutdown = true; clean_shutdown = true;
break; break;
} }
/* write diagnostic message if can't reach server */ /* write diagnostic message if can't reach server */
if ( still_connecting() if ( still_connecting() && ( !network->shutdown_in_progress() )
&& (!network->shutdown_in_progress()) && ( timestamp() - network->get_latest_remote_state().timestamp > 250 ) ) {
&& (timestamp() - network->get_latest_remote_state().timestamp > 250) ) { if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) {
if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) { if ( !network->shutdown_in_progress() ) {
if ( !network->shutdown_in_progress() ) { overlays.get_notification_engine().set_notification_string(
overlays.get_notification_engine().set_notification_string( std::wstring( L"Timed out waiting for server..." ), true ); std::wstring( L"Timed out waiting for server..." ), true );
network->start_shutdown(); network->start_shutdown();
} }
} else { } else {
overlays.get_notification_engine().set_notification_string( connecting_notification ); overlays.get_notification_engine().set_notification_string( connecting_notification );
} }
} else if ( (network->get_remote_state_num() != 0) } else if ( ( network->get_remote_state_num() != 0 )
&& (overlays.get_notification_engine().get_notification_string() && ( overlays.get_notification_engine().get_notification_string() == connecting_notification ) ) {
== connecting_notification) ) { overlays.get_notification_engine().set_notification_string( L"" );
overlays.get_notification_engine().set_notification_string( L"" );
} }
network->tick(); network->tick();
std::string & send_error = network->get_send_error(); std::string& send_error = network->get_send_error();
if ( !send_error.empty() ) { if ( !send_error.empty() ) {
overlays.get_notification_engine().set_network_error( send_error ); overlays.get_notification_engine().set_network_error( send_error );
send_error.clear(); send_error.clear();
} else { } else {
overlays.get_notification_engine().clear_network_error(); overlays.get_notification_engine().clear_network_error();
} }
} catch ( const Network::NetworkException &e ) { } catch ( const Network::NetworkException& e ) {
if ( !network->shutdown_in_progress() ) { if ( !network->shutdown_in_progress() ) {
overlays.get_notification_engine().set_network_error( e.what() ); overlays.get_notification_engine().set_network_error( e.what() );
} }
@@ -564,11 +569,11 @@ bool STMClient::main( void )
req.tv_nsec = 200000000; /* 0.2 sec */ req.tv_nsec = 200000000; /* 0.2 sec */
nanosleep( &req, NULL ); nanosleep( &req, NULL );
freeze_timestamp(); freeze_timestamp();
} catch ( const Crypto::CryptoException &e ) { } catch ( const Crypto::CryptoException& e ) {
if ( e.fatal ) { if ( e.fatal ) {
throw; throw;
} else { } else {
wchar_t tmp[ 128 ]; wchar_t tmp[128];
swprintf( tmp, 128, L"Crypto exception: %s", e.what() ); swprintf( tmp, 128, L"Crypto exception: %s", e.what() );
overlays.get_notification_engine().set_notification_string( std::wstring( tmp ) ); overlays.get_notification_engine().set_notification_string( std::wstring( tmp ) );
} }
@@ -576,4 +581,3 @@ bool STMClient::main( void )
} }
return clean_shutdown; return clean_shutdown;
} }
+26 -31
View File
@@ -33,18 +33,19 @@
#ifndef STM_CLIENT_HPP #ifndef STM_CLIENT_HPP
#define STM_CLIENT_HPP #define STM_CLIENT_HPP
#include <string>
#include <memory> #include <memory>
#include <string>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <termios.h> #include <termios.h>
#include "src/statesync/completeterminal.h"
#include "src/network/networktransport.h"
#include "src/statesync/user.h"
#include "src/frontend/terminaloverlay.h" #include "src/frontend/terminaloverlay.h"
#include "src/network/networktransport.h"
#include "src/statesync/completeterminal.h"
#include "src/statesync/user.h"
class STMClient { class STMClient
{
private: private:
std::string ip; std::string ip;
std::string port; std::string port;
@@ -88,37 +89,31 @@ private:
void resume( void ); /* restore state after SIGCONT */ void resume( void ); /* restore state after SIGCONT */
public: public:
STMClient( const char *s_ip, const char *s_port, const char *s_key, const char *predict_mode, unsigned int s_verbose, const char *predict_overwrite ) STMClient( const char* s_ip,
: ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), const char* s_port,
key( s_key ? s_key : "" ), const char* s_key,
escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ), const char* predict_mode,
escape_requires_lf( false ), escape_key_help( L"?" ), unsigned int s_verbose,
saved_termios(), raw_termios(), const char* predict_overwrite )
window_size(), : ip( s_ip ? s_ip : "" ), port( s_port ? s_port : "" ), key( s_key ? s_key : "" ), escape_key( 0x1E ),
local_framebuffer( 1, 1 ), escape_pass_key( '^' ), escape_pass_key2( '^' ), escape_requires_lf( false ), escape_key_help( L"?" ),
new_state( 1, 1 ), saved_termios(), raw_termios(), window_size(), local_framebuffer( 1, 1 ), new_state( 1, 1 ), overlays(),
overlays(), network(), display( true ), /* use TERM environment var to initialize display */
network(), connecting_notification(), repaint_requested( false ), lf_entered( false ), quit_sequence_started( false ),
display( true ), /* use TERM environment var to initialize display */ clean_shutdown( false ), verbose( s_verbose )
connecting_notification(),
repaint_requested( false ),
lf_entered( false ),
quit_sequence_started( false ),
clean_shutdown( false ),
verbose( s_verbose )
{ {
if ( predict_mode ) { if ( predict_mode ) {
if ( !strcmp( predict_mode, "always" ) ) { if ( !strcmp( predict_mode, "always" ) ) {
overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Always ); overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Always );
} else if ( !strcmp( predict_mode, "never" ) ) { } else if ( !strcmp( predict_mode, "never" ) ) {
overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Never ); overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Never );
} else if ( !strcmp( predict_mode, "adaptive" ) ) { } else if ( !strcmp( predict_mode, "adaptive" ) ) {
overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Adaptive ); overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Adaptive );
} else if ( !strcmp( predict_mode, "experimental" ) ) { } else if ( !strcmp( predict_mode, "experimental" ) ) {
overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Experimental ); overlays.get_prediction_engine().set_display_preference( Overlay::PredictionEngine::Experimental );
} else { } else {
fprintf( stderr, "Unknown prediction mode %s.\n", predict_mode ); fprintf( stderr, "Unknown prediction mode %s.\n", predict_mode );
exit( 1 ); exit( 1 );
} }
} }
if ( predict_overwrite && !strcmp( predict_overwrite, "yes" ) ) { if ( predict_overwrite && !strcmp( predict_overwrite, "yes" ) ) {
@@ -131,8 +126,8 @@ public:
bool main( void ); bool main( void );
/* unused */ /* unused */
STMClient( const STMClient & ); STMClient( const STMClient& );
STMClient & operator=( const STMClient & ); STMClient& operator=( const STMClient& );
}; };
#endif #endif
File diff suppressed because it is too large Load Diff
+282 -274
View File
@@ -33,308 +33,316 @@
#ifndef TERMINAL_OVERLAY_HPP #ifndef TERMINAL_OVERLAY_HPP
#define TERMINAL_OVERLAY_HPP #define TERMINAL_OVERLAY_HPP
#include "src/terminal/terminalframebuffer.h"
#include "src/network/network.h" #include "src/network/network.h"
#include "src/network/transportsender.h" #include "src/network/transportsender.h"
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
#include "src/terminal/terminalframebuffer.h"
#include <climits> #include <climits>
#include <vector> #include <vector>
namespace Overlay { namespace Overlay {
using namespace Terminal; using namespace Terminal;
using namespace Network; using namespace Network;
enum Validity { enum Validity
Pending, {
Correct, Pending,
CorrectNoCredit, Correct,
IncorrectOrExpired, CorrectNoCredit,
Inactive IncorrectOrExpired,
Inactive
};
class ConditionalOverlay
{
public:
uint64_t expiration_frame;
int col;
bool active; /* represents a prediction at all */
uint64_t tentative_until_epoch; /* when to show */
uint64_t prediction_time; /* used to find long-pending predictions */
ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative )
: expiration_frame( s_exp ), col( s_col ), active( false ), tentative_until_epoch( s_tentative ),
prediction_time( uint64_t( -1 ) )
{}
virtual ~ConditionalOverlay() {}
bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; }
void reset( void )
{
expiration_frame = tentative_until_epoch = -1;
active = false;
}
void expire( uint64_t s_exp, uint64_t now )
{
expiration_frame = s_exp;
prediction_time = now;
}
};
class ConditionalCursorMove : public ConditionalOverlay
{
public:
int row;
void apply( Framebuffer& fb, uint64_t confirmed_epoch ) const;
Validity get_validity( const Framebuffer& fb, uint64_t early_ack, uint64_t late_ack ) const;
ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative )
: ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row )
{}
};
class ConditionalOverlayCell : public ConditionalOverlay
{
public:
Cell replacement;
bool unknown;
std::vector<Cell> original_contents; /* we don't give credit for correct predictions
that match the original contents */
void apply( Framebuffer& fb, uint64_t confirmed_epoch, int row, bool flag ) const;
Validity get_validity( const Framebuffer& fb, int row, uint64_t early_ack, uint64_t late_ack ) const;
ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative )
: ConditionalOverlay( s_exp, s_col, s_tentative ), replacement( 0 ), unknown( false ), original_contents()
{}
void reset( void )
{
unknown = false;
original_contents.clear();
ConditionalOverlay::reset();
}
void reset_with_orig( void )
{
if ( ( !active ) || unknown ) {
reset();
return;
}
original_contents.push_back( replacement );
ConditionalOverlay::reset();
}
};
class ConditionalOverlayRow
{
public:
int row_num;
using overlay_cells_type = std::vector<ConditionalOverlayCell>;
overlay_cells_type overlay_cells;
void apply( Framebuffer& fb, uint64_t confirmed_epoch, bool flag ) const;
ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {}
};
/* the various overlays */
class NotificationEngine
{
private:
uint64_t last_word_from_server;
uint64_t last_acked_state;
std::string escape_key_string;
std::wstring message;
bool message_is_network_error;
uint64_t message_expiration;
bool show_quit_keystroke;
bool server_late( uint64_t ts ) const { return ( ts - last_word_from_server ) > 6500; }
bool reply_late( uint64_t ts ) const { return ( ts - last_acked_state ) > 10000; }
bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); }
public:
void adjust_message( void );
void apply( Framebuffer& fb ) const;
const std::wstring& get_notification_string( void ) const { return message; }
void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; }
void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; }
int wait_time( void ) const;
void set_notification_string( const std::wstring& s_message,
bool permanent = false,
bool s_show_quit_keystroke = true )
{
message = s_message;
if ( permanent ) {
message_expiration = -1;
} else {
message_expiration = timestamp() + 1000;
}
message_is_network_error = false;
show_quit_keystroke = s_show_quit_keystroke;
}
void set_escape_key_string( const std::string& s_name )
{
char tmp[128];
snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() );
escape_key_string = tmp;
}
void set_network_error( const std::string& s )
{
wchar_t tmp[128];
swprintf( tmp, 128, L"%s", s.c_str() );
message = tmp;
message_is_network_error = true;
message_expiration = timestamp() + Network::ACK_INTERVAL + 100;
}
void clear_network_error()
{
if ( message_is_network_error ) {
message_expiration = std::min( message_expiration, timestamp() + 1000 );
}
}
NotificationEngine();
};
class PredictionEngine
{
private:
static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */
static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */
static const uint64_t FLAG_TRIGGER_LOW = 50; /* <= ms cures flagging */
static const uint64_t FLAG_TRIGGER_HIGH = 80; /* > ms starts flagging */
static const uint64_t GLITCH_THRESHOLD = 250; /* prediction outstanding this long is glitch */
static const uint64_t GLITCH_REPAIR_COUNT = 10; /* non-glitches required to cure glitch trigger */
static const uint64_t GLITCH_REPAIR_MININTERVAL = 150; /* required time in between non-glitches */
static const uint64_t GLITCH_FLAG_THRESHOLD = 5000; /* prediction outstanding this long => underline */
char last_byte;
Parser::UTF8Parser parser;
using overlays_type = std::list<ConditionalOverlayRow>;
overlays_type overlays;
using cursors_type = std::list<ConditionalCursorMove>;
cursors_type cursors;
using overlay_cells_type = ConditionalOverlayRow::overlay_cells_type;
uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked;
ConditionalOverlayRow& get_or_make_row( int row_num, int num_cols );
uint64_t prediction_epoch;
uint64_t confirmed_epoch;
void become_tentative( void );
void newline_carriage_return( const Framebuffer& fb );
bool flagging; /* whether we are underlining predictions */
bool srtt_trigger; /* show predictions because of slow round trip time */
unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */
uint64_t last_quick_confirmation;
ConditionalCursorMove& cursor( void )
{
assert( !cursors.empty() );
return cursors.back();
}
void kill_epoch( uint64_t epoch, const Framebuffer& fb );
void init_cursor( const Framebuffer& fb );
unsigned int send_interval;
int last_height, last_width;
public:
enum DisplayPreference
{
Always,
Never,
Adaptive,
Experimental
}; };
class ConditionalOverlay { private:
public: DisplayPreference display_preference;
uint64_t expiration_frame; bool predict_overwrite;
int col;
bool active; /* represents a prediction at all */
uint64_t tentative_until_epoch; /* when to show */
uint64_t prediction_time; /* used to find long-pending predictions */
ConditionalOverlay( uint64_t s_exp, int s_col, uint64_t s_tentative ) bool active( void ) const;
: expiration_frame( s_exp ), col( s_col ),
active( false ),
tentative_until_epoch( s_tentative ), prediction_time( uint64_t( -1 ) )
{}
virtual ~ConditionalOverlay() {} bool timing_tests_necessary( void ) const
{
/* Are there any timing-based triggers that haven't fired yet? */
return !( glitch_trigger && flagging );
}
bool tentative( uint64_t confirmed_epoch ) const { return tentative_until_epoch > confirmed_epoch; } public:
void reset( void ) { expiration_frame = tentative_until_epoch = -1; active = false; } void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; }
void expire( uint64_t s_exp, uint64_t now ) void set_predict_overwrite( bool overwrite ) { predict_overwrite = overwrite; }
{
expiration_frame = s_exp; prediction_time = now;
}
};
class ConditionalCursorMove : public ConditionalOverlay { void apply( Framebuffer& fb ) const;
public: void new_user_byte( char the_byte, const Framebuffer& fb );
int row; void cull( const Framebuffer& fb );
void apply( Framebuffer &fb, uint64_t confirmed_epoch ) const; void reset( void );
Validity get_validity( const Framebuffer &fb, uint64_t early_ack, uint64_t late_ack ) const; void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; }
void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; }
void set_local_frame_late_acked( uint64_t x ) { local_frame_late_acked = x; }
ConditionalCursorMove( uint64_t s_exp, int s_row, int s_col, uint64_t s_tentative ) void set_send_interval( unsigned int x ) { send_interval = x; }
: ConditionalOverlay( s_exp, s_col, s_tentative ), row( s_row )
{}
};
class ConditionalOverlayCell : public ConditionalOverlay { int wait_time( void ) const { return ( timing_tests_necessary() && active() ) ? 50 : INT_MAX; }
public:
Cell replacement;
bool unknown;
std::vector<Cell> original_contents; /* we don't give credit for correct predictions PredictionEngine( void )
that match the original contents */ : last_byte( 0 ), parser(), overlays(), cursors(), local_frame_sent( 0 ), local_frame_acked( 0 ),
local_frame_late_acked( 0 ), prediction_epoch( 1 ), confirmed_epoch( 0 ), flagging( false ),
srtt_trigger( false ), glitch_trigger( 0 ), last_quick_confirmation( 0 ), send_interval( 250 ),
last_height( 0 ), last_width( 0 ), display_preference( Adaptive ), predict_overwrite( false )
{}
};
void apply( Framebuffer &fb, uint64_t confirmed_epoch, int row, bool flag ) const; class TitleEngine
Validity get_validity( const Framebuffer &fb, int row, uint64_t early_ack, uint64_t late_ack ) const; {
private:
Terminal::Framebuffer::title_type prefix;
ConditionalOverlayCell( uint64_t s_exp, int s_col, uint64_t s_tentative ) public:
: ConditionalOverlay( s_exp, s_col, s_tentative ), void apply( Framebuffer& fb ) const { fb.prefix_window_title( prefix ); }
replacement( 0 ), TitleEngine() : prefix() {}
unknown( false ), void set_prefix( const std::wstring& s );
original_contents() };
{}
void reset( void ) { unknown = false; original_contents.clear(); ConditionalOverlay::reset(); } /* the overlay manager */
void reset_with_orig( void ) { class OverlayManager
if ( (!active) || unknown ) { {
reset(); private:
return; NotificationEngine notifications;
} PredictionEngine predictions;
TitleEngine title;
original_contents.push_back( replacement ); public:
ConditionalOverlay::reset(); void apply( Framebuffer& fb );
}
};
class ConditionalOverlayRow { NotificationEngine& get_notification_engine( void ) { return notifications; }
public: PredictionEngine& get_prediction_engine( void ) { return predictions; }
int row_num;
using overlay_cells_type = std::vector<ConditionalOverlayCell>; void set_title_prefix( const std::wstring& s ) { title.set_prefix( s ); }
overlay_cells_type overlay_cells;
void apply( Framebuffer &fb, uint64_t confirmed_epoch, bool flag ) const; OverlayManager() : notifications(), predictions(), title() {}
ConditionalOverlayRow( int s_row_num ) : row_num( s_row_num ), overlay_cells() {} int wait_time( void ) const { return std::min( notifications.wait_time(), predictions.wait_time() ); }
}; };
/* the various overlays */
class NotificationEngine {
private:
uint64_t last_word_from_server;
uint64_t last_acked_state;
std::string escape_key_string;
std::wstring message;
bool message_is_network_error;
uint64_t message_expiration;
bool show_quit_keystroke;
bool server_late( uint64_t ts ) const { return (ts - last_word_from_server) > 6500; }
bool reply_late( uint64_t ts ) const { return (ts - last_acked_state) > 10000; }
bool need_countup( uint64_t ts ) const { return server_late( ts ) || reply_late( ts ); }
public:
void adjust_message( void );
void apply( Framebuffer &fb ) const;
const std::wstring &get_notification_string( void ) const { return message; }
void server_heard( uint64_t s_last_word ) { last_word_from_server = s_last_word; }
void server_acked( uint64_t s_last_acked ) { last_acked_state = s_last_acked; }
int wait_time( void ) const;
void set_notification_string( const std::wstring &s_message, bool permanent = false, bool s_show_quit_keystroke = true )
{
message = s_message;
if ( permanent ) {
message_expiration = -1;
} else {
message_expiration = timestamp() + 1000;
}
message_is_network_error = false;
show_quit_keystroke = s_show_quit_keystroke;
}
void set_escape_key_string( const std::string &s_name )
{
char tmp[ 128 ];
snprintf( tmp, sizeof tmp, " [To quit: %s .]", s_name.c_str() );
escape_key_string = tmp;
}
void set_network_error( const std::string &s )
{
wchar_t tmp[ 128 ];
swprintf( tmp, 128, L"%s", s.c_str() );
message = tmp;
message_is_network_error = true;
message_expiration = timestamp() + Network::ACK_INTERVAL + 100;
}
void clear_network_error()
{
if ( message_is_network_error ) {
message_expiration = std::min( message_expiration, timestamp() + 1000 );
}
}
NotificationEngine();
};
class PredictionEngine {
private:
static const uint64_t SRTT_TRIGGER_LOW = 20; /* <= ms cures SRTT trigger to show predictions */
static const uint64_t SRTT_TRIGGER_HIGH = 30; /* > ms starts SRTT trigger */
static const uint64_t FLAG_TRIGGER_LOW = 50; /* <= ms cures flagging */
static const uint64_t FLAG_TRIGGER_HIGH = 80; /* > ms starts flagging */
static const uint64_t GLITCH_THRESHOLD = 250; /* prediction outstanding this long is glitch */
static const uint64_t GLITCH_REPAIR_COUNT = 10; /* non-glitches required to cure glitch trigger */
static const uint64_t GLITCH_REPAIR_MININTERVAL = 150; /* required time in between non-glitches */
static const uint64_t GLITCH_FLAG_THRESHOLD = 5000; /* prediction outstanding this long => underline */
char last_byte;
Parser::UTF8Parser parser;
using overlays_type = std::list<ConditionalOverlayRow>;
overlays_type overlays;
using cursors_type = std::list<ConditionalCursorMove>;
cursors_type cursors;
using overlay_cells_type = ConditionalOverlayRow::overlay_cells_type;
uint64_t local_frame_sent, local_frame_acked, local_frame_late_acked;
ConditionalOverlayRow & get_or_make_row( int row_num, int num_cols );
uint64_t prediction_epoch;
uint64_t confirmed_epoch;
void become_tentative( void );
void newline_carriage_return( const Framebuffer &fb );
bool flagging; /* whether we are underlining predictions */
bool srtt_trigger; /* show predictions because of slow round trip time */
unsigned int glitch_trigger; /* show predictions temporarily because of long-pending prediction */
uint64_t last_quick_confirmation;
ConditionalCursorMove & cursor( void ) { assert( !cursors.empty() ); return cursors.back(); }
void kill_epoch( uint64_t epoch, const Framebuffer &fb );
void init_cursor( const Framebuffer &fb );
unsigned int send_interval;
int last_height, last_width;
public:
enum DisplayPreference {
Always,
Never,
Adaptive,
Experimental
};
private:
DisplayPreference display_preference;
bool predict_overwrite;
bool active( void ) const;
bool timing_tests_necessary( void ) const {
/* Are there any timing-based triggers that haven't fired yet? */
return !( glitch_trigger && flagging );
}
public:
void set_display_preference( DisplayPreference s_pref ) { display_preference = s_pref; }
void set_predict_overwrite( bool overwrite ) { predict_overwrite = overwrite; }
void apply( Framebuffer &fb ) const;
void new_user_byte( char the_byte, const Framebuffer &fb );
void cull( const Framebuffer &fb );
void reset( void );
void set_local_frame_sent( uint64_t x ) { local_frame_sent = x; }
void set_local_frame_acked( uint64_t x ) { local_frame_acked = x; }
void set_local_frame_late_acked( uint64_t x ) { local_frame_late_acked = x; }
void set_send_interval( unsigned int x ) { send_interval = x; }
int wait_time( void ) const
{
return ( timing_tests_necessary() && active() )
? 50
: INT_MAX;
}
PredictionEngine( void ) : last_byte( 0 ), parser(), overlays(), cursors(),
local_frame_sent( 0 ), local_frame_acked( 0 ),
local_frame_late_acked( 0 ),
prediction_epoch( 1 ), confirmed_epoch( 0 ),
flagging( false ),
srtt_trigger( false ),
glitch_trigger( 0 ),
last_quick_confirmation( 0 ),
send_interval( 250 ),
last_height( 0 ), last_width( 0 ),
display_preference( Adaptive ),
predict_overwrite( false )
{
}
};
class TitleEngine {
private:
Terminal::Framebuffer::title_type prefix;
public:
void apply( Framebuffer &fb ) const { fb.prefix_window_title( prefix ); }
TitleEngine() : prefix() {}
void set_prefix( const std::wstring &s );
};
/* the overlay manager */
class OverlayManager {
private:
NotificationEngine notifications;
PredictionEngine predictions;
TitleEngine title;
public:
void apply( Framebuffer &fb );
NotificationEngine & get_notification_engine( void ) { return notifications; }
PredictionEngine & get_prediction_engine( void ) { return predictions; }
void set_title_prefix( const std::wstring &s ) { title.set_prefix( s ); }
OverlayManager() : notifications(), predictions(), title() {}
int wait_time( void ) const
{
return std::min( notifications.wait_time(), predictions.wait_time() );
}
};
} }
#endif #endif
+9 -8
View File
@@ -1,17 +1,18 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "src/terminal/parser.h"
#include "src/statesync/completeterminal.h" #include "src/statesync/completeterminal.h"
#include "src/terminal/parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, size_t size )
Terminal::Display display(false); {
Terminal::Complete complete(80, 24); Terminal::Display display( false );
Terminal::Framebuffer state(80, 24); Terminal::Complete complete( 80, 24 );
for (size_t i = 0; i < size; i++) { Terminal::Framebuffer state( 80, 24 );
complete.act(Parser::UserByte(data[i])); for ( size_t i = 0; i < size; i++ ) {
complete.act( Parser::UserByte( data[i] ) );
} }
display.new_frame(true, state, complete.get_fb()); display.new_frame( true, state, complete.get_fb() );
return 0; return 0;
} }
+4 -3
View File
@@ -3,12 +3,13 @@
#include "src/terminal/parser.h" #include "src/terminal/parser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, size_t size )
{
Parser::UTF8Parser parser; Parser::UTF8Parser parser;
Parser::Actions result; Parser::Actions result;
for (size_t i = 0; i < size; i++) { for ( size_t i = 0; i < size; i++ ) {
parser.input(data[i], result); parser.input( data[i], result );
} }
return 0; return 0;
+9 -11
View File
@@ -37,26 +37,24 @@
using namespace Network; using namespace Network;
std::string Compressor::compress_str( const std::string &input ) std::string Compressor::compress_str( const std::string& input )
{ {
long unsigned int len = BUFFER_SIZE; long unsigned int len = BUFFER_SIZE;
dos_assert( Z_OK == compress( buffer, &len, dos_assert( Z_OK
reinterpret_cast<const unsigned char *>( input.data() ), == compress( buffer, &len, reinterpret_cast<const unsigned char*>( input.data() ), input.size() ) );
input.size() ) ); return std::string( reinterpret_cast<char*>( buffer ), len );
return std::string( reinterpret_cast<char *>( buffer ), len );
} }
std::string Compressor::uncompress_str( const std::string &input ) std::string Compressor::uncompress_str( const std::string& input )
{ {
long unsigned int len = BUFFER_SIZE; long unsigned int len = BUFFER_SIZE;
dos_assert( Z_OK == uncompress( buffer, &len, dos_assert( Z_OK
reinterpret_cast<const unsigned char *>( input.data() ), == uncompress( buffer, &len, reinterpret_cast<const unsigned char*>( input.data() ), input.size() ) );
input.size() ) ); return std::string( reinterpret_cast<char*>( buffer ), len );
return std::string( reinterpret_cast<char *>( buffer ), len );
} }
/* construct on first use */ /* construct on first use */
Compressor & Network::get_compressor( void ) Compressor& Network::get_compressor( void )
{ {
static Compressor the_compressor; static Compressor the_compressor;
return the_compressor; return the_compressor;
+14 -13
View File
@@ -36,25 +36,26 @@
#include <string> #include <string>
namespace Network { namespace Network {
class Compressor { class Compressor
private: {
static const int BUFFER_SIZE = 2048 * 2048; /* effective limit on terminal size */ private:
static const int BUFFER_SIZE = 2048 * 2048; /* effective limit on terminal size */
unsigned char buffer[BUFFER_SIZE]; unsigned char buffer[BUFFER_SIZE];
public: public:
Compressor() : buffer() {} Compressor() : buffer() {}
~Compressor() {} ~Compressor() {}
std::string compress_str( const std::string &input ); std::string compress_str( const std::string& input );
std::string uncompress_str( const std::string &input ); std::string uncompress_str( const std::string& input );
/* unused */ /* unused */
Compressor( const Compressor & ); Compressor( const Compressor& );
Compressor & operator=( const Compressor & ); Compressor& operator=( const Compressor& );
}; };
Compressor & get_compressor( void ); Compressor& get_compressor( void );
} }
#endif #endif
+142 -176
View File
@@ -36,8 +36,8 @@
#include <cerrno> #include <cerrno>
#include <cstring> #include <cstring>
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h>
#ifdef HAVE_SYS_UIO_H #ifdef HAVE_SYS_UIO_H
#include <sys/uio.h> #include <sys/uio.h>
#endif #endif
@@ -45,11 +45,11 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <unistd.h> #include <unistd.h>
#include "src/crypto/byteorder.h"
#include "src/crypto/crypto.h"
#include "src/network/network.h"
#include "src/util/dos_assert.h" #include "src/util/dos_assert.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/crypto/byteorder.h"
#include "src/network/network.h"
#include "src/crypto/crypto.h"
#include "src/util/timestamp.h" #include "src/util/timestamp.h"
@@ -64,22 +64,20 @@
using namespace Network; using namespace Network;
using namespace Crypto; using namespace Crypto;
const uint64_t DIRECTION_MASK = uint64_t(1) << 63; const uint64_t DIRECTION_MASK = uint64_t( 1 ) << 63;
const uint64_t SEQUENCE_MASK = uint64_t(-1) ^ DIRECTION_MASK; const uint64_t SEQUENCE_MASK = uint64_t( -1 ) ^ DIRECTION_MASK;
/* Read in packet */ /* Read in packet */
Packet::Packet( const Message & message ) Packet::Packet( const Message& message )
: seq( message.nonce.val() & SEQUENCE_MASK ), : seq( message.nonce.val() & SEQUENCE_MASK ),
direction( (message.nonce.val() & DIRECTION_MASK) ? TO_CLIENT : TO_SERVER ), direction( ( message.nonce.val() & DIRECTION_MASK ) ? TO_CLIENT : TO_SERVER ), timestamp( -1 ),
timestamp( -1 ), timestamp_reply( -1 ), payload()
timestamp_reply( -1 ),
payload()
{ {
dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) ); dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) );
const uint16_t *data = (uint16_t *)message.text.data(); const uint16_t* data = (uint16_t*)message.text.data();
timestamp = be16toh( data[ 0 ] ); timestamp = be16toh( data[0] );
timestamp_reply = be16toh( data[ 1 ] ); timestamp_reply = be16toh( data[1] );
payload = std::string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() ); payload = std::string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() );
} }
@@ -87,17 +85,17 @@ Packet::Packet( const Message & message )
/* Output from packet */ /* Output from packet */
Message Packet::toMessage( void ) Message Packet::toMessage( void )
{ {
uint64_t direction_seq = (uint64_t( direction == TO_CLIENT ) << 63) | (seq & SEQUENCE_MASK); uint64_t direction_seq = ( uint64_t( direction == TO_CLIENT ) << 63 ) | ( seq & SEQUENCE_MASK );
uint16_t ts_net[ 2 ] = { static_cast<uint16_t>( htobe16( timestamp ) ), uint16_t ts_net[2]
static_cast<uint16_t>( htobe16( timestamp_reply ) ) }; = { static_cast<uint16_t>( htobe16( timestamp ) ), static_cast<uint16_t>( htobe16( timestamp_reply ) ) };
std::string timestamps = std::string( (char *)ts_net, 2 * sizeof( uint16_t ) ); std::string timestamps = std::string( (char*)ts_net, 2 * sizeof( uint16_t ) );
return Message( Nonce( direction_seq ), timestamps + payload ); return Message( Nonce( direction_seq ), timestamps + payload );
} }
Packet Connection::new_packet( const std::string &s_payload ) Packet Connection::new_packet( const std::string& s_payload )
{ {
uint16_t outgoing_timestamp_reply = -1; uint16_t outgoing_timestamp_reply = -1;
@@ -105,7 +103,7 @@ Packet Connection::new_packet( const std::string &s_payload )
if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */ if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */
/* send "corrected" timestamp advanced by how long we held it */ /* send "corrected" timestamp advanced by how long we held it */
outgoing_timestamp_reply = saved_timestamp + (now - saved_timestamp_received_at); outgoing_timestamp_reply = saved_timestamp + ( now - saved_timestamp_received_at );
saved_timestamp = -1; saved_timestamp = -1;
saved_timestamp_received_at = 0; saved_timestamp_received_at = 0;
} }
@@ -133,7 +131,7 @@ void Connection::prune_sockets( void )
if ( timestamp() - last_port_choice > MAX_OLD_SOCKET_AGE ) { if ( timestamp() - last_port_choice > MAX_OLD_SOCKET_AGE ) {
int num_to_kill = socks.size() - 1; int num_to_kill = socks.size() - 1;
for ( int i = 0; i < num_to_kill; i++ ) { for ( int i = 0; i < num_to_kill; i++ ) {
socks.pop_front(); socks.pop_front();
} }
} }
} else { } else {
@@ -149,8 +147,7 @@ void Connection::prune_sockets( void )
} }
} }
Connection::Socket::Socket( int family ) Connection::Socket::Socket( int family ) : _fd( socket( family, SOCK_DGRAM, 0 ) )
: _fd( socket( family, SOCK_DGRAM, 0 ) )
{ {
if ( _fd < 0 ) { if ( _fd < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
@@ -185,13 +182,11 @@ void Connection::setup( void )
last_port_choice = timestamp(); last_port_choice = timestamp();
} }
const std::vector< int > Connection::fds( void ) const const std::vector<int> Connection::fds( void ) const
{ {
std::vector< int > ret; std::vector<int> ret;
for ( std::deque< Socket >::const_iterator it = socks.begin(); for ( std::deque<Socket>::const_iterator it = socks.begin(); it != socks.end(); it++ ) {
it != socks.end();
it++ ) {
ret.push_back( it->fd() ); ret.push_back( it->fd() );
} }
@@ -201,54 +196,42 @@ const std::vector< int > Connection::fds( void ) const
void Connection::set_MTU( int family ) void Connection::set_MTU( int family )
{ {
switch ( family ) { switch ( family ) {
case AF_INET: case AF_INET:
MTU = DEFAULT_IPV4_MTU - IPV4_HEADER_LEN; MTU = DEFAULT_IPV4_MTU - IPV4_HEADER_LEN;
break; break;
case AF_INET6: case AF_INET6:
MTU = DEFAULT_IPV6_MTU - IPV6_HEADER_LEN; MTU = DEFAULT_IPV6_MTU - IPV6_HEADER_LEN;
break; break;
default: default:
throw NetworkException( "Unknown address family", 0 ); throw NetworkException( "Unknown address family", 0 );
} }
} }
class AddrInfo { class AddrInfo
{
public: public:
struct addrinfo *res; struct addrinfo* res;
AddrInfo( const char *node, const char *service, AddrInfo( const char* node, const char* service, const struct addrinfo* hints ) : res( NULL )
const struct addrinfo *hints ) : {
res( NULL ) {
int errcode = getaddrinfo( node, service, hints, &res ); int errcode = getaddrinfo( node, service, hints, &res );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "Bad IP address (" ) + (node != NULL ? node : "(null)") + "): " + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "Bad IP address (" ) + ( node != NULL ? node : "(null)" )
+ "): " + gai_strerror( errcode ),
0 );
} }
} }
~AddrInfo() { freeaddrinfo(res); } ~AddrInfo() { freeaddrinfo( res ); }
private: private:
AddrInfo(const AddrInfo &); AddrInfo( const AddrInfo& );
AddrInfo &operator=(const AddrInfo &); AddrInfo& operator=( const AddrInfo& );
}; };
Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */ Connection::Connection( const char* desired_ip, const char* desired_port ) /* server */
: socks(), : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( true ), MTU( DEFAULT_SEND_MTU ),
has_remote_addr( false ), key(), session( key ), direction( TO_CLIENT ), saved_timestamp( -1 ), saved_timestamp_received_at( 0 ),
remote_addr(), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ), last_roundtrip_success( -1 ),
remote_addr_len( 0 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), send_error()
server( true ),
MTU( DEFAULT_SEND_MTU ),
key(),
session( key ),
direction( TO_CLIENT ),
saved_timestamp( -1 ),
saved_timestamp_received_at( 0 ),
expected_receiver_seq( 0 ),
last_heard( -1 ),
last_port_choice( -1 ),
last_roundtrip_success( -1 ),
RTT_hit( false ),
SRTT( 1000 ),
RTTVAR( 500 ),
send_error()
{ {
setup(); setup();
@@ -263,33 +246,34 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
int desired_port_high = -1; int desired_port_high = -1;
if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) { if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) {
throw NetworkException("Invalid port range", 0); throw NetworkException( "Invalid port range", 0 );
} }
/* try to bind to desired IP first */ /* try to bind to desired IP first */
if ( desired_ip ) { if ( desired_ip ) {
try { try {
if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) { return; } if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) {
} catch ( const NetworkException &e ) { return;
fprintf( stderr, "Error binding to IP %s: %s\n", }
desired_ip, } catch ( const NetworkException& e ) {
e.what() ); fprintf( stderr, "Error binding to IP %s: %s\n", desired_ip, e.what() );
} }
} }
/* now try any local interface */ /* now try any local interface */
try { try {
if ( try_bind( NULL, desired_port_low, desired_port_high ) ) { return; } if ( try_bind( NULL, desired_port_low, desired_port_high ) ) {
} catch ( const NetworkException &e ) { return;
fprintf( stderr, "Error binding to any interface: %s\n", }
e.what() ); } catch ( const NetworkException& e ) {
fprintf( stderr, "Error binding to any interface: %s\n", e.what() );
throw; /* this time it's fatal */ throw; /* this time it's fatal */
} }
throw NetworkException( "Could not bind", errno ); throw NetworkException( "Could not bind", errno );
} }
bool Connection::try_bind( const char *addr, int port_low, int port_high ) bool Connection::try_bind( const char* addr, int port_low, int port_high )
{ {
struct addrinfo hints; struct addrinfo hints;
memset( &hints, 0, sizeof( hints ) ); memset( &hints, 0, sizeof( hints ) );
@@ -313,21 +297,21 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
socks.push_back( Socket( local_addr.sa.sa_family ) ); socks.push_back( Socket( local_addr.sa.sa_family ) );
for ( int i = search_low; i <= search_high; i++ ) { for ( int i = search_low; i <= search_high; i++ ) {
switch (local_addr.sa.sa_family) { switch ( local_addr.sa.sa_family ) {
case AF_INET: case AF_INET:
local_addr.sin.sin_port = htons( i ); local_addr.sin.sin_port = htons( i );
break; break;
case AF_INET6: case AF_INET6:
local_addr.sin6.sin6_port = htons( i ); local_addr.sin6.sin6_port = htons( i );
break; break;
default: default:
throw NetworkException( "Unknown address family", 0 ); throw NetworkException( "Unknown address family", 0 );
} }
if ( local_addr.sa.sa_family == AF_INET6 if ( local_addr.sa.sa_family == AF_INET6
&& memcmp(&local_addr.sin6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 ) { && memcmp( &local_addr.sin6.sin6_addr, &in6addr_any, sizeof( in6addr_any ) ) == 0 ) {
const int off = 0; const int off = 0;
if ( setsockopt( sock(), IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off) ) ) { if ( setsockopt( sock(), IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof( off ) ) ) {
perror( "setsockopt( IPV6_V6ONLY, off )" ); perror( "setsockopt( IPV6_V6ONLY, off )" );
} }
} }
@@ -339,38 +323,26 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
} }
int saved_errno = errno; int saved_errno = errno;
socks.pop_back(); socks.pop_back();
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; char host[NI_MAXHOST], serv[NI_MAXSERV];
int errcode = getnameinfo( &local_addr.sa, local_addr_len, int errcode = getnameinfo( &local_addr.sa,
host, sizeof( host ), serv, sizeof( serv ), local_addr_len,
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); host,
sizeof( host ),
serv,
sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
fprintf( stderr, "Failed binding to %s:%s\n", fprintf( stderr, "Failed binding to %s:%s\n", host, serv );
host, serv );
throw NetworkException( "bind", saved_errno ); throw NetworkException( "bind", saved_errno );
} }
Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */ Connection::Connection( const char* key_str, const char* ip, const char* port ) /* client */
: socks(), : socks(), has_remote_addr( false ), remote_addr(), remote_addr_len( 0 ), server( false ),
has_remote_addr( false ), MTU( DEFAULT_SEND_MTU ), key( key_str ), session( key ), direction( TO_SERVER ), saved_timestamp( -1 ),
remote_addr(), saved_timestamp_received_at( 0 ), expected_receiver_seq( 0 ), last_heard( -1 ), last_port_choice( -1 ),
remote_addr_len( 0 ), last_roundtrip_success( -1 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ), send_error()
server( false ),
MTU( DEFAULT_SEND_MTU ),
key( key_str ),
session( key ),
direction( TO_SERVER ),
saved_timestamp( -1 ),
saved_timestamp_received_at( 0 ),
expected_receiver_seq( 0 ),
last_heard( -1 ),
last_port_choice( -1 ),
last_roundtrip_success( -1 ),
RTT_hit( false ),
SRTT( 1000 ),
RTTVAR( 500 ),
send_error()
{ {
setup(); setup();
@@ -392,7 +364,7 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
set_MTU( remote_addr.sa.sa_family ); set_MTU( remote_addr.sa.sa_family );
} }
void Connection::send( const std::string & s ) void Connection::send( const std::string& s )
{ {
if ( !has_remote_addr ) { if ( !has_remote_addr ) {
return; return;
@@ -402,8 +374,7 @@ void Connection::send( const std::string & s )
std::string p = session.encrypt( px.toMessage() ); std::string p = session.encrypt( px.toMessage() );
ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT, ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT, &remote_addr.sa, remote_addr_len );
&remote_addr.sa, remote_addr_len );
if ( bytes_sent != static_cast<ssize_t>( p.size() ) ) { if ( bytes_sent != static_cast<ssize_t>( p.size() ) ) {
/* Make sendto() failure available to the frontend. */ /* Make sendto() failure available to the frontend. */
@@ -422,8 +393,7 @@ void Connection::send( const std::string & s )
fprintf( stderr, "Server now detached from client.\n" ); fprintf( stderr, "Server now detached from client.\n" );
} }
} else { /* client */ } else { /* client */
if ( ( now - last_port_choice > PORT_HOP_INTERVAL ) if ( ( now - last_port_choice > PORT_HOP_INTERVAL ) && ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) {
&& ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) {
hop_port(); hop_port();
} }
} }
@@ -432,18 +402,15 @@ void Connection::send( const std::string & s )
std::string Connection::recv( void ) std::string Connection::recv( void )
{ {
assert( !socks.empty() ); assert( !socks.empty() );
for ( std::deque< Socket >::const_iterator it = socks.begin(); for ( std::deque<Socket>::const_iterator it = socks.begin(); it != socks.end(); it++ ) {
it != socks.end();
it++ ) {
std::string payload; std::string payload;
try { try {
payload = recv_one( it->fd()); payload = recv_one( it->fd() );
} catch ( NetworkException & e ) { } catch ( NetworkException& e ) {
if ( (e.the_errno == EAGAIN) if ( ( e.the_errno == EAGAIN ) || ( e.the_errno == EWOULDBLOCK ) ) {
|| (e.the_errno == EWOULDBLOCK) ) { continue;
continue;
} else { } else {
throw; throw;
} }
} }
@@ -461,8 +428,8 @@ std::string Connection::recv_one( int sock_to_recv )
struct msghdr header; struct msghdr header;
struct iovec msg_iovec; struct iovec msg_iovec;
char msg_payload[ Session::RECEIVE_MTU ]; char msg_payload[Session::RECEIVE_MTU];
char msg_control[ Session::RECEIVE_MTU ]; char msg_control[Session::RECEIVE_MTU];
/* receive source address */ /* receive source address */
header.msg_name = &packet_remote_addr; header.msg_name = &packet_remote_addr;
@@ -494,32 +461,32 @@ std::string Connection::recv_one( int sock_to_recv )
/* receive ECN */ /* receive ECN */
bool congestion_experienced = false; bool congestion_experienced = false;
struct cmsghdr *ecn_hdr = CMSG_FIRSTHDR( &header ); struct cmsghdr* ecn_hdr = CMSG_FIRSTHDR( &header );
if ( ecn_hdr if ( ecn_hdr && ecn_hdr->cmsg_level == IPPROTO_IP
&& ecn_hdr->cmsg_level == IPPROTO_IP
&& ( ecn_hdr->cmsg_type == IP_TOS && ( ecn_hdr->cmsg_type == IP_TOS
#ifdef IP_RECVTOS #ifdef IP_RECVTOS
|| ecn_hdr->cmsg_type == IP_RECVTOS || ecn_hdr->cmsg_type == IP_RECVTOS
#endif #endif
) ) { ) ) {
/* got one */ /* got one */
uint8_t *ecn_octet_p = (uint8_t *)CMSG_DATA( ecn_hdr ); uint8_t* ecn_octet_p = (uint8_t*)CMSG_DATA( ecn_hdr );
assert( ecn_octet_p ); assert( ecn_octet_p );
congestion_experienced = (*ecn_octet_p & 0x03) == 0x03; congestion_experienced = ( *ecn_octet_p & 0x03 ) == 0x03;
} }
Packet p( session.decrypt( msg_payload, received_len ) ); Packet p( session.decrypt( msg_payload, received_len ) );
dos_assert( p.direction == (server ? TO_SERVER : TO_CLIENT) ); /* prevent malicious playback to sender */ dos_assert( p.direction == ( server ? TO_SERVER : TO_CLIENT ) ); /* prevent malicious playback to sender */
if ( p.seq < expected_receiver_seq ) { /* don't use (but do return) out-of-order packets for timestamp or targeting */ if ( p.seq
< expected_receiver_seq ) { /* don't use (but do return) out-of-order packets for timestamp or targeting */
return p.payload; return p.payload;
} }
expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise
screw up the timestamp and targeting */ screw up the timestamp and targeting */
if ( p.timestamp != uint16_t(-1) ) { if ( p.timestamp != uint16_t( -1 ) ) {
saved_timestamp = p.timestamp; saved_timestamp = p.timestamp;
saved_timestamp_received_at = timestamp(); saved_timestamp_received_at = timestamp();
@@ -528,26 +495,26 @@ std::string Connection::recv_one( int sock_to_recv )
/* this will gradually slow the counterparty down to the minimum frame rate */ /* this will gradually slow the counterparty down to the minimum frame rate */
saved_timestamp -= CONGESTION_TIMESTAMP_PENALTY; saved_timestamp -= CONGESTION_TIMESTAMP_PENALTY;
if ( server ) { if ( server ) {
fprintf( stderr, "Received explicit congestion notification.\n" ); fprintf( stderr, "Received explicit congestion notification.\n" );
} }
} }
} }
if ( p.timestamp_reply != uint16_t(-1) ) { if ( p.timestamp_reply != uint16_t( -1 ) ) {
uint16_t now = timestamp16(); uint16_t now = timestamp16();
double R = timestamp_diff( now, p.timestamp_reply ); double R = timestamp_diff( now, p.timestamp_reply );
if ( R < 5000 ) { /* ignore large values, e.g. server was Ctrl-Zed */ if ( R < 5000 ) { /* ignore large values, e.g. server was Ctrl-Zed */
if ( !RTT_hit ) { /* first measurement */ if ( !RTT_hit ) { /* first measurement */
SRTT = R; SRTT = R;
RTTVAR = R / 2; RTTVAR = R / 2;
RTT_hit = true; RTT_hit = true;
} else { } else {
const double alpha = 1.0 / 8.0; const double alpha = 1.0 / 8.0;
const double beta = 1.0 / 4.0; const double beta = 1.0 / 4.0;
RTTVAR = (1 - beta) * RTTVAR + ( beta * fabs( SRTT - R ) ); RTTVAR = ( 1 - beta ) * RTTVAR + ( beta * fabs( SRTT - R ) );
SRTT = (1 - alpha) * SRTT + ( alpha * R ); SRTT = ( 1 - alpha ) * SRTT + ( alpha * R );
} }
} }
} }
@@ -557,19 +524,22 @@ std::string Connection::recv_one( int sock_to_recv )
last_heard = timestamp(); last_heard = timestamp();
if ( server && /* only client can roam */ if ( server && /* only client can roam */
( remote_addr_len != header.msg_namelen || ( remote_addr_len != header.msg_namelen
memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) ) { || memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) ) {
remote_addr = packet_remote_addr; remote_addr = packet_remote_addr;
remote_addr_len = header.msg_namelen; remote_addr_len = header.msg_namelen;
char host[ NI_MAXHOST ], serv[ NI_MAXSERV ]; char host[NI_MAXHOST], serv[NI_MAXSERV];
int errcode = getnameinfo( &remote_addr.sa, remote_addr_len, int errcode = getnameinfo( &remote_addr.sa,
host, sizeof( host ), serv, sizeof( serv ), remote_addr_len,
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV ); host,
sizeof( host ),
serv,
sizeof( serv ),
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
fprintf( stderr, "Server now attached to client at %s:%s\n", fprintf( stderr, "Server now attached to client at %s:%s\n", host, serv );
host, serv );
} }
return p.payload; return p.payload;
} }
@@ -583,10 +553,8 @@ std::string Connection::port( void ) const
throw NetworkException( "getsockname", errno ); throw NetworkException( "getsockname", errno );
} }
char serv[ NI_MAXSERV ]; char serv[NI_MAXSERV];
int errcode = getnameinfo( &local_addr.sa, addrlen, int errcode = getnameinfo( &local_addr.sa, addrlen, NULL, 0, serv, sizeof( serv ), NI_DGRAM | NI_NUMERICSERV );
NULL, 0, serv, sizeof( serv ),
NI_DGRAM | NI_NUMERICSERV );
if ( errcode != 0 ) { if ( errcode != 0 ) {
throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 ); throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 );
} }
@@ -602,7 +570,7 @@ uint64_t Network::timestamp( void )
uint16_t Network::timestamp16( void ) uint16_t Network::timestamp16( void )
{ {
uint16_t ts = timestamp() % 65536; uint16_t ts = timestamp() % 65536;
if ( ts == uint16_t(-1) ) { if ( ts == uint16_t( -1 ) ) {
ts++; ts++;
} }
return ts; return ts;
@@ -634,18 +602,17 @@ uint64_t Connection::timeout( void ) const
Connection::Socket::~Socket() Connection::Socket::~Socket()
{ {
fatal_assert ( close( _fd ) == 0 ); fatal_assert( close( _fd ) == 0 );
} }
Connection::Socket::Socket( const Socket & other ) Connection::Socket::Socket( const Socket& other ) : _fd( dup( other._fd ) )
: _fd( dup( other._fd ) )
{ {
if ( _fd < 0 ) { if ( _fd < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
} }
} }
Connection::Socket & Connection::Socket::operator=( const Socket & other ) Connection::Socket& Connection::Socket::operator=( const Socket& other )
{ {
if ( dup2( other._fd, _fd ) < 0 ) { if ( dup2( other._fd, _fd ) < 0 ) {
throw NetworkException( "socket", errno ); throw NetworkException( "socket", errno );
@@ -654,39 +621,39 @@ Connection::Socket & Connection::Socket::operator=( const Socket & other )
return *this; return *this;
} }
bool Connection::parse_portrange( const char * desired_port, int & desired_port_low, int & desired_port_high ) bool Connection::parse_portrange( const char* desired_port, int& desired_port_low, int& desired_port_high )
{ {
/* parse "port" or "portlow:porthigh" */ /* parse "port" or "portlow:porthigh" */
desired_port_low = desired_port_high = 0; desired_port_low = desired_port_high = 0;
char *end; char* end;
long value; long value;
/* parse first (only?) port */ /* parse first (only?) port */
errno = 0; errno = 0;
value = strtol( desired_port, &end, 10 ); value = strtol( desired_port, &end, 10 );
if ( (errno != 0) || (*end != '\0' && *end != ':') ) { if ( ( errno != 0 ) || ( *end != '\0' && *end != ':' ) ) {
fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port ); fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port );
return false; return false;
} }
if ( (value < 0) || (value > 65535) ) { if ( ( value < 0 ) || ( value > 65535 ) ) {
fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value ); fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value );
return false; return false;
} }
desired_port_low = (int)value; desired_port_low = (int)value;
if (*end == '\0') { /* not a port range */ if ( *end == '\0' ) { /* not a port range */
desired_port_high = desired_port_low; desired_port_high = desired_port_low;
return true; return true;
} }
/* port range; parse high port */ /* port range; parse high port */
const char * cp = end + 1; const char* cp = end + 1;
errno = 0; errno = 0;
value = strtol( cp, &end, 10 ); value = strtol( cp, &end, 10 );
if ( (errno != 0) || (*end != '\0') ) { if ( ( errno != 0 ) || ( *end != '\0' ) ) {
fprintf( stderr, "Invalid high port number (%s)\n", cp ); fprintf( stderr, "Invalid high port number (%s)\n", cp );
return false; return false;
} }
if ( (value < 0) || (value > 65535) ) { if ( ( value < 0 ) || ( value > 65535 ) ) {
fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value ); fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value );
return false; return false;
} }
@@ -702,6 +669,5 @@ bool Connection::parse_portrange( const char * desired_port, int & desired_port_
return false; return false;
} }
return true; return true;
} }
+184 -178
View File
@@ -42,198 +42,204 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h>
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
using namespace Crypto; using namespace Crypto;
namespace Network { namespace Network {
static const unsigned int MOSH_PROTOCOL_VERSION = 2; /* bumped for echo-ack */ static const unsigned int MOSH_PROTOCOL_VERSION = 2; /* bumped for echo-ack */
uint64_t timestamp( void ); uint64_t timestamp( void );
uint16_t timestamp16( void ); uint16_t timestamp16( void );
uint16_t timestamp_diff( uint16_t tsnew, uint16_t tsold ); uint16_t timestamp_diff( uint16_t tsnew, uint16_t tsold );
class NetworkException : public std::exception { class NetworkException : public std::exception
public: {
std::string function; public:
int the_errno; std::string function;
int the_errno;
private:
std::string my_what;
public:
NetworkException( std::string s_function = "<none>", int s_errno = 0 )
: function( s_function ), the_errno( s_errno ), my_what( function + ": " + strerror( the_errno ) )
{}
const char* what() const throw() { return my_what.c_str(); }
~NetworkException() throw() {}
};
enum Direction
{
TO_SERVER = 0,
TO_CLIENT = 1
};
class Packet
{
public:
const uint64_t seq;
Direction direction;
uint16_t timestamp, timestamp_reply;
std::string payload;
Packet( Direction s_direction, uint16_t s_timestamp, uint16_t s_timestamp_reply, const std::string& s_payload )
: seq( Crypto::unique() ), direction( s_direction ), timestamp( s_timestamp ),
timestamp_reply( s_timestamp_reply ), payload( s_payload )
{}
Packet( const Message& message );
Message toMessage( void );
};
union Addr {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_storage ss;
};
class Connection
{
private:
/*
* For IPv4, guess the typical (minimum) header length;
* fragmentation is not dangerous, just inefficient.
*/
static const int IPV4_HEADER_LEN = 20 /* base IP header */
+ 8 /* UDP */;
/*
* For IPv6, we don't want to ever have MTU issues, so make a
* conservative guess about header size.
*/
static const int IPV6_HEADER_LEN = 40 /* base IPv6 header */
+ 16 /* 2 minimum-sized extension headers */
+ 8 /* UDP */;
/* Application datagram MTU. For constructors and fallback. */
static const int DEFAULT_SEND_MTU = 500;
/*
* IPv4 MTU. Don't use full Ethernet-derived MTU,
* mobile networks have high tunneling overhead.
*
* As of July 2016, VPN traffic over Amtrak Acela wifi seems to be
* dropped if tunnelled packets are 1320 bytes or larger. Use a
* 1280-byte IPv4 MTU for now.
*
* We may have to implement ICMP-less PMTUD (RFC 4821) eventually.
*/
static const int DEFAULT_IPV4_MTU = 1280;
/* IPv6 MTU. Use the guaranteed minimum to avoid fragmentation. */
static const int DEFAULT_IPV6_MTU = 1280;
static const uint64_t MIN_RTO = 50; /* ms */
static const uint64_t MAX_RTO = 1000; /* ms */
static const int PORT_RANGE_LOW = 60001;
static const int PORT_RANGE_HIGH = 60999;
static const unsigned int SERVER_ASSOCIATION_TIMEOUT = 40000;
static const unsigned int PORT_HOP_INTERVAL = 10000;
static const unsigned int MAX_PORTS_OPEN = 10;
static const unsigned int MAX_OLD_SOCKET_AGE = 60000;
static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */
bool try_bind( const char* addr, int port_low, int port_high );
class Socket
{
private: private:
std::string my_what; int _fd;
public:
NetworkException( std::string s_function="<none>", int s_errno=0)
: function( s_function ), the_errno( s_errno ),
my_what(function + ": " + strerror(the_errno)) {}
const char *what() const throw () { return my_what.c_str(); }
~NetworkException() throw () {}
};
enum Direction {
TO_SERVER = 0,
TO_CLIENT = 1
};
class Packet {
public:
const uint64_t seq;
Direction direction;
uint16_t timestamp, timestamp_reply;
std::string payload;
Packet( Direction s_direction,
uint16_t s_timestamp, uint16_t s_timestamp_reply, const std::string & s_payload )
: seq( Crypto::unique() ), direction( s_direction ),
timestamp( s_timestamp ), timestamp_reply( s_timestamp_reply ), payload( s_payload )
{}
Packet( const Message & message );
Message toMessage( void );
};
union Addr {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_storage ss;
};
class Connection {
private:
/*
* For IPv4, guess the typical (minimum) header length;
* fragmentation is not dangerous, just inefficient.
*/
static const int IPV4_HEADER_LEN = 20 /* base IP header */
+ 8 /* UDP */;
/*
* For IPv6, we don't want to ever have MTU issues, so make a
* conservative guess about header size.
*/
static const int IPV6_HEADER_LEN = 40 /* base IPv6 header */
+ 16 /* 2 minimum-sized extension headers */
+ 8 /* UDP */;
/* Application datagram MTU. For constructors and fallback. */
static const int DEFAULT_SEND_MTU = 500;
/*
* IPv4 MTU. Don't use full Ethernet-derived MTU,
* mobile networks have high tunneling overhead.
*
* As of July 2016, VPN traffic over Amtrak Acela wifi seems to be
* dropped if tunnelled packets are 1320 bytes or larger. Use a
* 1280-byte IPv4 MTU for now.
*
* We may have to implement ICMP-less PMTUD (RFC 4821) eventually.
*/
static const int DEFAULT_IPV4_MTU = 1280;
/* IPv6 MTU. Use the guaranteed minimum to avoid fragmentation. */
static const int DEFAULT_IPV6_MTU = 1280;
static const uint64_t MIN_RTO = 50; /* ms */
static const uint64_t MAX_RTO = 1000; /* ms */
static const int PORT_RANGE_LOW = 60001;
static const int PORT_RANGE_HIGH = 60999;
static const unsigned int SERVER_ASSOCIATION_TIMEOUT = 40000;
static const unsigned int PORT_HOP_INTERVAL = 10000;
static const unsigned int MAX_PORTS_OPEN = 10;
static const unsigned int MAX_OLD_SOCKET_AGE = 60000;
static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */
bool try_bind( const char *addr, int port_low, int port_high );
class Socket
{
private:
int _fd;
public:
int fd( void ) const { return _fd; }
Socket( int family );
~Socket();
Socket( const Socket & other );
Socket & operator=( const Socket & other );
};
std::deque< Socket > socks;
bool has_remote_addr;
Addr remote_addr;
socklen_t remote_addr_len;
bool server;
int MTU; /* application datagram MTU */
Base64Key key;
Session session;
void setup( void );
Direction direction;
uint16_t saved_timestamp;
uint64_t saved_timestamp_received_at;
uint64_t expected_receiver_seq;
uint64_t last_heard;
uint64_t last_port_choice;
uint64_t last_roundtrip_success; /* transport layer needs to tell us this */
bool RTT_hit;
double SRTT;
double RTTVAR;
/* Error from send()/sendto(). */
std::string send_error;
Packet new_packet( const std::string &s_payload );
void hop_port( void );
int sock( void ) const { assert( !socks.empty() ); return socks.back().fd(); }
void prune_sockets( void );
std::string recv_one( int sock_to_recv );
void set_MTU( int family );
public: public:
/* Network transport overhead. */ int fd( void ) const { return _fd; }
static const int ADDED_BYTES = 8 /* seqno/nonce */ + 4 /* timestamps */; Socket( int family );
~Socket();
Connection( const char *desired_ip, const char *desired_port ); /* server */ Socket( const Socket& other );
Connection( const char *key_str, const char *ip, const char *port ); /* client */ Socket& operator=( const Socket& other );
void send( const std::string & s );
std::string recv( void );
const std::vector< int > fds( void ) const;
int get_MTU( void ) const { return MTU; }
std::string port( void ) const;
std::string get_key( void ) const { return key.printable_key(); }
bool get_has_remote_addr( void ) const { return has_remote_addr; }
uint64_t timeout( void ) const;
double get_SRTT( void ) const { return SRTT; }
const Addr &get_remote_addr( void ) const { return remote_addr; }
socklen_t get_remote_addr_len( void ) const { return remote_addr_len; }
std::string &get_send_error( void )
{
return send_error;
}
void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; }
static bool parse_portrange( const char * desired_port_range, int & desired_port_low, int & desired_port_high );
}; };
std::deque<Socket> socks;
bool has_remote_addr;
Addr remote_addr;
socklen_t remote_addr_len;
bool server;
int MTU; /* application datagram MTU */
Base64Key key;
Session session;
void setup( void );
Direction direction;
uint16_t saved_timestamp;
uint64_t saved_timestamp_received_at;
uint64_t expected_receiver_seq;
uint64_t last_heard;
uint64_t last_port_choice;
uint64_t last_roundtrip_success; /* transport layer needs to tell us this */
bool RTT_hit;
double SRTT;
double RTTVAR;
/* Error from send()/sendto(). */
std::string send_error;
Packet new_packet( const std::string& s_payload );
void hop_port( void );
int sock( void ) const
{
assert( !socks.empty() );
return socks.back().fd();
}
void prune_sockets( void );
std::string recv_one( int sock_to_recv );
void set_MTU( int family );
public:
/* Network transport overhead. */
static const int ADDED_BYTES = 8 /* seqno/nonce */ + 4 /* timestamps */;
Connection( const char* desired_ip, const char* desired_port ); /* server */
Connection( const char* key_str, const char* ip, const char* port ); /* client */
void send( const std::string& s );
std::string recv( void );
const std::vector<int> fds( void ) const;
int get_MTU( void ) const { return MTU; }
std::string port( void ) const;
std::string get_key( void ) const { return key.printable_key(); }
bool get_has_remote_addr( void ) const { return has_remote_addr; }
uint64_t timeout( void ) const;
double get_SRTT( void ) const { return SRTT; }
const Addr& get_remote_addr( void ) const { return remote_addr; }
socklen_t get_remote_addr_len( void ) const { return remote_addr_len; }
std::string& get_send_error( void ) { return send_error; }
void set_last_roundtrip_success( uint64_t s_success ) { last_roundtrip_success = s_success; }
static bool parse_portrange( const char* desired_port_range, int& desired_port_low, int& desired_port_high );
};
} }
#endif #endif
+60 -52
View File
@@ -39,35 +39,32 @@
using namespace Network; using namespace Network;
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote, Transport<MyState, RemoteState>::Transport( MyState& initial_state,
const char *desired_ip, const char *desired_port ) RemoteState& initial_remote,
: connection( desired_ip, desired_port ), const char* desired_ip,
sender( &connection, initial_state ), const char* desired_port )
: connection( desired_ip, desired_port ), sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ), received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
receiver_quench_timer( 0 ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( 0 )
last_receiver_state( initial_remote ),
fragments(),
verbose( 0 )
{ {
/* server */ /* server */
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote, Transport<MyState, RemoteState>::Transport( MyState& initial_state,
const char *key_str, const char *ip, const char *port ) RemoteState& initial_remote,
: connection( key_str, ip, port ), const char* key_str,
sender( &connection, initial_state ), const char* ip,
const char* port )
: connection( key_str, ip, port ), sender( &connection, initial_state ),
received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ), received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
receiver_quench_timer( 0 ), receiver_quench_timer( 0 ), last_receiver_state( initial_remote ), fragments(), verbose( 0 )
last_receiver_state( initial_remote ),
fragments(),
verbose( 0 )
{ {
/* client */ /* client */
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
void Transport<MyState, RemoteState>::recv( void ) void Transport<MyState, RemoteState>::recv( void )
{ {
std::string s( connection.recv() ); std::string s( connection.recv() );
@@ -86,27 +83,28 @@ void Transport<MyState, RemoteState>::recv( void )
connection.set_last_roundtrip_success( sender.get_sent_state_acked_timestamp() ); connection.set_last_roundtrip_success( sender.get_sent_state_acked_timestamp() );
/* first, make sure we don't already have the new state */ /* first, make sure we don't already have the new state */
for ( typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); for ( typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
i != received_states.end(); i != received_states.end();
i++ ) { i++ ) {
if ( inst.new_num() == i->num ) { if ( inst.new_num() == i->num ) {
return; return;
} }
} }
/* now, make sure we do have the old state */ /* now, make sure we do have the old state */
bool found = 0; bool found = 0;
typename std::list< TimestampedState<RemoteState> >::iterator reference_state = received_states.begin(); typename std::list<TimestampedState<RemoteState>>::iterator reference_state = received_states.begin();
while ( reference_state != received_states.end() ) { while ( reference_state != received_states.end() ) {
if ( inst.old_num() == reference_state->num ) { if ( inst.old_num() == reference_state->num ) {
found = true; found = true;
break; break;
} }
reference_state++; reference_state++;
} }
if ( !found ) { if ( !found ) {
// fprintf( stderr, "Ignoring out-of-order packet. Reference state %d has been discarded or hasn't yet been received.\n", int(inst.old_num) ); // fprintf( stderr, "Ignoring out-of-order packet. Reference state %d has been discarded or hasn't yet been
// received.\n", int(inst.old_num) );
return; /* this is security-sensitive and part of how we enforce idempotency */ return; /* this is security-sensitive and part of how we enforce idempotency */
} }
@@ -120,13 +118,16 @@ void Transport<MyState, RemoteState>::recv( void )
if ( received_states.size() > 1024 ) { /* limit on state queue */ if ( received_states.size() > 1024 ) { /* limit on state queue */
uint64_t now = timestamp(); uint64_t now = timestamp();
if ( now < receiver_quench_timer ) { /* deny letting state grow further */ if ( now < receiver_quench_timer ) { /* deny letting state grow further */
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Receiver queue full, discarding %d (malicious sender or long-unidirectional connectivity?)\n", fprintf(
(unsigned int)(timestamp() % 100000), (int)inst.new_num() ); stderr,
} "[%u] Receiver queue full, discarding %d (malicious sender or long-unidirectional connectivity?)\n",
return; (unsigned int)( timestamp() % 100000 ),
(int)inst.new_num() );
}
return;
} else { } else {
receiver_quench_timer = now + 15000; receiver_quench_timer = now + 15000;
} }
} }
@@ -140,21 +141,28 @@ void Transport<MyState, RemoteState>::recv( void )
} }
/* Insert new state in sorted place */ /* Insert new state in sorted place */
for ( typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); for ( typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
i != received_states.end(); i != received_states.end();
i++ ) { i++ ) {
if ( i->num > new_state.num ) { if ( i->num > new_state.num ) {
received_states.insert( i, new_state ); received_states.insert( i, new_state );
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Received OUT-OF-ORDER state %d [ack %d]\n", fprintf( stderr,
(unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.ack_num() ); "[%u] Received OUT-OF-ORDER state %d [ack %d]\n",
} (unsigned int)( timestamp() % 100000 ),
return; (int)new_state.num,
(int)inst.ack_num() );
}
return;
} }
} }
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Received state %d [coming from %d, ack %d]\n", fprintf( stderr,
(unsigned int)(timestamp() % 100000), (int)new_state.num, (int)inst.old_num(), (int)inst.ack_num() ); "[%u] Received state %d [coming from %d, ack %d]\n",
(unsigned int)( timestamp() % 100000 ),
(int)new_state.num,
(int)inst.old_num(),
(int)inst.ack_num() );
} }
received_states.push_back( new_state ); received_states.push_back( new_state );
sender.set_ack_num( received_states.back().num ); sender.set_ack_num( received_states.back().num );
@@ -167,12 +175,12 @@ void Transport<MyState, RemoteState>::recv( void )
} }
/* The sender uses throwaway_num to tell us the earliest received state that we need to keep around */ /* The sender uses throwaway_num to tell us the earliest received state that we need to keep around */
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwaway_num ) void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwaway_num )
{ {
typename std::list< TimestampedState<RemoteState> >::iterator i = received_states.begin(); typename std::list<TimestampedState<RemoteState>>::iterator i = received_states.begin();
while ( i != received_states.end() ) { while ( i != received_states.end() ) {
typename std::list< TimestampedState<RemoteState> >::iterator inext = i; typename std::list<TimestampedState<RemoteState>>::iterator inext = i;
inext++; inext++;
if ( i->num < throwaway_num ) { if ( i->num < throwaway_num ) {
received_states.erase( i ); received_states.erase( i );
@@ -183,18 +191,18 @@ void Transport<MyState, RemoteState>::process_throwaway_until( uint64_t throwawa
fatal_assert( received_states.size() > 0 ); fatal_assert( received_states.size() > 0 );
} }
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
std::string Transport<MyState, RemoteState>::get_remote_diff( void ) std::string Transport<MyState, RemoteState>::get_remote_diff( void )
{ {
/* find diff between last receiver state and current remote state, then rationalize states */ /* find diff between last receiver state and current remote state, then rationalize states */
std::string ret( received_states.back().state.diff_from( last_receiver_state ) ); std::string ret( received_states.back().state.diff_from( last_receiver_state ) );
const RemoteState *oldest_receiver_state = &received_states.front().state; const RemoteState* oldest_receiver_state = &received_states.front().state;
for ( typename std::list< TimestampedState<RemoteState> >::reverse_iterator i = received_states.rbegin(); for ( typename std::list<TimestampedState<RemoteState>>::reverse_iterator i = received_states.rbegin();
i != received_states.rend(); i != received_states.rend();
i++ ) { i++ ) {
i->state.subtract( oldest_receiver_state ); i->state.subtract( oldest_receiver_state );
} }
+79 -70
View File
@@ -44,82 +44,91 @@
#include "transportfragment.h" #include "transportfragment.h"
namespace Network { namespace Network {
template <class MyState, class RemoteState> template<class MyState, class RemoteState>
class Transport class Transport
{
private:
/* the underlying, encrypted network connection */
Connection connection;
/* sender side */
TransportSender<MyState> sender;
/* helper methods for recv() */
void process_throwaway_until( uint64_t throwaway_num );
/* simple receiver */
std::list<TimestampedState<RemoteState>> received_states;
uint64_t receiver_quench_timer;
RemoteState last_receiver_state; /* the state we were in when user last queried state */
FragmentAssembly fragments;
unsigned int verbose;
public:
Transport( MyState& initial_state,
RemoteState& initial_remote,
const char* desired_ip,
const char* desired_port );
Transport( MyState& initial_state,
RemoteState& initial_remote,
const char* key_str,
const char* ip,
const char* port );
/* Send data or an ack if necessary. */
void tick( void ) { sender.tick(); }
/* Returns the number of ms to wait until next possible event. */
int wait_time( void ) { return sender.wait_time(); }
/* Blocks waiting for a packet. */
void recv( void );
/* Find diff between last receiver state and current remote state, then rationalize states. */
std::string get_remote_diff( void );
/* Shut down other side of connection. */
/* Illegal to change current_state after this. */
void start_shutdown( void ) { sender.start_shutdown(); }
bool shutdown_in_progress( void ) const { return sender.get_shutdown_in_progress(); }
bool shutdown_acknowledged( void ) const { return sender.get_shutdown_acknowledged(); }
bool shutdown_ack_timed_out( void ) const { return sender.shutdown_ack_timed_out(); }
bool has_remote_addr( void ) const { return connection.get_has_remote_addr(); }
/* Other side has requested shutdown and we have sent one ACK */
bool counterparty_shutdown_ack_sent( void ) const { return sender.get_counterparty_shutdown_acknowledged(); }
std::string port( void ) const { return connection.port(); }
std::string get_key( void ) const { return connection.get_key(); }
MyState& get_current_state( void ) { return sender.get_current_state(); }
void set_current_state( const MyState& x ) { sender.set_current_state( x ); }
uint64_t get_remote_state_num( void ) const { return received_states.back().num; }
const TimestampedState<RemoteState>& get_latest_remote_state( void ) const { return received_states.back(); }
const std::vector<int> fds( void ) const { return connection.fds(); }
void set_verbose( unsigned int s_verbose )
{ {
private: sender.set_verbose( s_verbose );
/* the underlying, encrypted network connection */ verbose = s_verbose;
Connection connection; }
/* sender side */ void set_send_delay( int new_delay ) { sender.set_send_delay( new_delay ); }
TransportSender<MyState> sender;
/* helper methods for recv() */ uint64_t get_sent_state_acked_timestamp( void ) const { return sender.get_sent_state_acked_timestamp(); }
void process_throwaway_until( uint64_t throwaway_num ); uint64_t get_sent_state_acked( void ) const { return sender.get_sent_state_acked(); }
uint64_t get_sent_state_last( void ) const { return sender.get_sent_state_last(); }
/* simple receiver */ unsigned int send_interval( void ) const { return sender.send_interval(); }
std::list< TimestampedState<RemoteState> > received_states;
uint64_t receiver_quench_timer;
RemoteState last_receiver_state; /* the state we were in when user last queried state */
FragmentAssembly fragments;
unsigned int verbose;
public: const Addr& get_remote_addr( void ) const { return connection.get_remote_addr(); }
Transport( MyState &initial_state, RemoteState &initial_remote, socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); }
const char *desired_ip, const char *desired_port );
Transport( MyState &initial_state, RemoteState &initial_remote,
const char *key_str, const char *ip, const char *port );
/* Send data or an ack if necessary. */ std::string& get_send_error( void ) { return connection.get_send_error(); }
void tick( void ) { sender.tick(); } };
/* Returns the number of ms to wait until next possible event. */
int wait_time( void ) { return sender.wait_time(); }
/* Blocks waiting for a packet. */
void recv( void );
/* Find diff between last receiver state and current remote state, then rationalize states. */
std::string get_remote_diff( void );
/* Shut down other side of connection. */
/* Illegal to change current_state after this. */
void start_shutdown( void ) { sender.start_shutdown(); }
bool shutdown_in_progress( void ) const { return sender.get_shutdown_in_progress(); }
bool shutdown_acknowledged( void ) const { return sender.get_shutdown_acknowledged(); }
bool shutdown_ack_timed_out( void ) const { return sender.shutdown_ack_timed_out(); }
bool has_remote_addr( void ) const { return connection.get_has_remote_addr(); }
/* Other side has requested shutdown and we have sent one ACK */
bool counterparty_shutdown_ack_sent( void ) const { return sender.get_counterparty_shutdown_acknowledged(); }
std::string port( void ) const { return connection.port(); }
std::string get_key( void ) const { return connection.get_key(); }
MyState &get_current_state( void ) { return sender.get_current_state(); }
void set_current_state( const MyState &x ) { sender.set_current_state( x ); }
uint64_t get_remote_state_num( void ) const { return received_states.back().num; }
const TimestampedState<RemoteState> & get_latest_remote_state( void ) const { return received_states.back(); }
const std::vector< int > fds( void ) const { return connection.fds(); }
void set_verbose( unsigned int s_verbose ) { sender.set_verbose( s_verbose ); verbose = s_verbose; }
void set_send_delay( int new_delay ) { sender.set_send_delay( new_delay ); }
uint64_t get_sent_state_acked_timestamp( void ) const { return sender.get_sent_state_acked_timestamp(); }
uint64_t get_sent_state_acked( void ) const { return sender.get_sent_state_acked(); }
uint64_t get_sent_state_last( void ) const { return sender.get_sent_state_last(); }
unsigned int send_interval( void ) const { return sender.send_interval(); }
const Addr &get_remote_addr( void ) const { return connection.get_remote_addr(); }
socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); }
std::string &get_send_error( void ) { return connection.get_send_error(); }
};
} }
#endif #endif
+24 -28
View File
@@ -32,11 +32,11 @@
#include <cassert> #include <cassert>
#include "src/crypto/byteorder.h"
#include "transportfragment.h"
#include "src/protobufs/transportinstruction.pb.h"
#include "compressor.h" #include "compressor.h"
#include "src/crypto/byteorder.h"
#include "src/protobufs/transportinstruction.pb.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "transportfragment.h"
using namespace Network; using namespace Network;
using namespace TransportBuffers; using namespace TransportBuffers;
@@ -44,13 +44,13 @@ using namespace TransportBuffers;
static std::string network_order_string( uint16_t host_order ) static std::string network_order_string( uint16_t host_order )
{ {
uint16_t net_int = htobe16( host_order ); uint16_t net_int = htobe16( host_order );
return std::string( (char *)&net_int, sizeof( net_int ) ); return std::string( (char*)&net_int, sizeof( net_int ) );
} }
static std::string network_order_string( uint64_t host_order ) static std::string network_order_string( uint64_t host_order )
{ {
uint64_t net_int = htobe64( host_order ); uint64_t net_int = htobe64( host_order );
return std::string( (char *)&net_int, sizeof( net_int ) ); return std::string( (char*)&net_int, sizeof( net_int ) );
} }
std::string Fragment::tostring( void ) std::string Fragment::tostring( void )
@@ -61,7 +61,8 @@ std::string Fragment::tostring( void )
ret += network_order_string( id ); ret += network_order_string( id );
fatal_assert( !( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */ fatal_assert(
!( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */
uint16_t combined_fragment_num = ( final << 15 ) | fragment_num; uint16_t combined_fragment_num = ( final << 15 ) | fragment_num;
ret += network_order_string( combined_fragment_num ); ret += network_order_string( combined_fragment_num );
@@ -72,23 +73,22 @@ std::string Fragment::tostring( void )
return ret; return ret;
} }
Fragment::Fragment( const std::string &x ) Fragment::Fragment( const std::string& x )
: id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), : id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), contents()
contents()
{ {
fatal_assert( x.size() >= frag_header_len ); fatal_assert( x.size() >= frag_header_len );
contents = std::string( x.begin() + frag_header_len, x.end() ); contents = std::string( x.begin() + frag_header_len, x.end() );
uint64_t data64; uint64_t data64;
uint16_t *data16 = (uint16_t *)x.data(); uint16_t* data16 = (uint16_t*)x.data();
memcpy( &data64, x.data(), sizeof( data64 ) ); memcpy( &data64, x.data(), sizeof( data64 ) );
id = be64toh( data64 ); id = be64toh( data64 );
fragment_num = be16toh( data16[ 4 ] ); fragment_num = be16toh( data16[4] );
final = ( fragment_num & 0x8000 ) >> 15; final = ( fragment_num & 0x8000 ) >> 15;
fragment_num &= 0x7FFF; fragment_num &= 0x7FFF;
} }
bool FragmentAssembly::add_fragment( Fragment &frag ) bool FragmentAssembly::add_fragment( Fragment& frag )
{ {
/* see if this is a totally new packet */ /* see if this is a totally new packet */
if ( current_id != frag.id ) { if ( current_id != frag.id ) {
@@ -98,15 +98,14 @@ bool FragmentAssembly::add_fragment( Fragment &frag )
fragments_arrived = 1; fragments_arrived = 1;
fragments_total = -1; /* unknown */ fragments_total = -1; /* unknown */
current_id = frag.id; current_id = frag.id;
} else { /* not a new packet */ } else { /* not a new packet */
/* see if we already have this fragment */ /* see if we already have this fragment */
if ( (fragments.size() > frag.fragment_num) if ( ( fragments.size() > frag.fragment_num ) && ( fragments.at( frag.fragment_num ).initialized ) ) {
&& (fragments.at( frag.fragment_num ).initialized) ) {
/* make sure new version is same as what we already have */ /* make sure new version is same as what we already have */
assert( fragments.at( frag.fragment_num ) == frag ); assert( fragments.at( frag.fragment_num ) == frag );
} else { } else {
if ( (int)fragments.size() < frag.fragment_num + 1 ) { if ( (int)fragments.size() < frag.fragment_num + 1 ) {
fragments.resize( frag.fragment_num + 1 ); fragments.resize( frag.fragment_num + 1 );
} }
fragments.at( frag.fragment_num ) = frag; fragments.at( frag.fragment_num ) = frag;
fragments_arrived++; fragments_arrived++;
@@ -148,27 +147,24 @@ Instruction FragmentAssembly::get_assembly( void )
return ret; return ret;
} }
bool Fragment::operator==( const Fragment &x ) const bool Fragment::operator==( const Fragment& x ) const
{ {
return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final ) return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final )
&& ( initialized == x.initialized ) && ( contents == x.contents ); && ( initialized == x.initialized ) && ( contents == x.contents );
} }
std::vector<Fragment> Fragmenter::make_fragments( const Instruction &inst, size_t MTU ) std::vector<Fragment> Fragmenter::make_fragments( const Instruction& inst, size_t MTU )
{ {
MTU -= Fragment::frag_header_len; MTU -= Fragment::frag_header_len;
if ( (inst.old_num() != last_instruction.old_num()) if ( ( inst.old_num() != last_instruction.old_num() ) || ( inst.new_num() != last_instruction.new_num() )
|| (inst.new_num() != last_instruction.new_num()) || ( inst.ack_num() != last_instruction.ack_num() )
|| (inst.ack_num() != last_instruction.ack_num()) || ( inst.throwaway_num() != last_instruction.throwaway_num() )
|| (inst.throwaway_num() != last_instruction.throwaway_num()) || ( inst.chaff() != last_instruction.chaff() )
|| (inst.chaff() != last_instruction.chaff()) || ( inst.protocol_version() != last_instruction.protocol_version() ) || ( last_MTU != MTU ) ) {
|| (inst.protocol_version() != last_instruction.protocol_version())
|| (last_MTU != MTU) ) {
next_instruction_id++; next_instruction_id++;
} }
if ( (inst.old_num() == last_instruction.old_num()) if ( ( inst.old_num() == last_instruction.old_num() ) && ( inst.new_num() == last_instruction.new_num() ) ) {
&& (inst.new_num() == last_instruction.new_num()) ) {
assert( inst.diff() == last_instruction.diff() ); assert( inst.diff() == last_instruction.diff() );
} }
+55 -58
View File
@@ -40,66 +40,63 @@
#include "src/protobufs/transportinstruction.pb.h" #include "src/protobufs/transportinstruction.pb.h"
namespace Network { namespace Network {
using namespace TransportBuffers; using namespace TransportBuffers;
class Fragment class Fragment
{
public:
static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t );
uint64_t id;
uint16_t fragment_num;
bool final;
bool initialized;
std::string contents;
Fragment() : id( -1 ), fragment_num( -1 ), final( false ), initialized( false ), contents() {}
Fragment( uint64_t s_id, uint16_t s_fragment_num, bool s_final, const std::string& s_contents )
: id( s_id ), fragment_num( s_fragment_num ), final( s_final ), initialized( true ), contents( s_contents )
{}
Fragment( const std::string& x );
std::string tostring( void );
bool operator==( const Fragment& x ) const;
};
class FragmentAssembly
{
private:
std::vector<Fragment> fragments;
uint64_t current_id;
int fragments_arrived, fragments_total;
public:
FragmentAssembly() : fragments(), current_id( -1 ), fragments_arrived( 0 ), fragments_total( -1 ) {}
bool add_fragment( Fragment& inst );
Instruction get_assembly( void );
};
class Fragmenter
{
private:
uint64_t next_instruction_id;
Instruction last_instruction;
size_t last_MTU;
public:
Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 )
{ {
public: last_instruction.set_old_num( -1 );
static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t ); last_instruction.set_new_num( -1 );
}
uint64_t id; std::vector<Fragment> make_fragments( const Instruction& inst, size_t MTU );
uint16_t fragment_num; uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); }
bool final; };
bool initialized;
std::string contents;
Fragment()
: id( -1 ), fragment_num( -1 ), final( false ), initialized( false ), contents()
{}
Fragment( uint64_t s_id, uint16_t s_fragment_num, bool s_final, const std::string & s_contents )
: id( s_id ), fragment_num( s_fragment_num ), final( s_final ), initialized( true ),
contents( s_contents )
{}
Fragment( const std::string &x );
std::string tostring( void );
bool operator==( const Fragment &x ) const;
};
class FragmentAssembly
{
private:
std::vector<Fragment> fragments;
uint64_t current_id;
int fragments_arrived, fragments_total;
public:
FragmentAssembly() : fragments(), current_id( -1 ), fragments_arrived( 0 ), fragments_total( -1 ) {}
bool add_fragment( Fragment &inst );
Instruction get_assembly( void );
};
class Fragmenter
{
private:
uint64_t next_instruction_id;
Instruction last_instruction;
size_t last_MTU;
public:
Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 )
{
last_instruction.set_old_num( -1 );
last_instruction.set_new_num( -1 );
}
std::vector<Fragment> make_fragments( const Instruction &inst, size_t MTU );
uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); }
};
} }
+71 -81
View File
@@ -46,30 +46,18 @@
using namespace Network; using namespace Network;
template <class MyState> template<class MyState>
TransportSender<MyState>::TransportSender( Connection *s_connection, MyState &initial_state ) TransportSender<MyState>::TransportSender( Connection* s_connection, MyState& initial_state )
: connection( s_connection ), : connection( s_connection ), current_state( initial_state ),
current_state( initial_state ),
sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ), sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ),
assumed_receiver_state( sent_states.begin() ), assumed_receiver_state( sent_states.begin() ), fragmenter(), next_ack_time( timestamp() ),
fragmenter(), next_send_time( timestamp() ), verbose( 0 ), shutdown_in_progress( false ), shutdown_tries( 0 ),
next_ack_time( timestamp() ), shutdown_start( -1 ), ack_num( 0 ), pending_data_ack( false ), SEND_MINDELAY( 8 ), last_heard( 0 ), prng(),
next_send_time( timestamp() ),
verbose( 0 ),
shutdown_in_progress( false ),
shutdown_tries( 0 ),
shutdown_start( -1 ),
ack_num( 0 ),
pending_data_ack( false ),
SEND_MINDELAY( 8 ),
last_heard( 0 ),
prng(),
mindelay_clock( -1 ) mindelay_clock( -1 )
{ {}
}
/* Try to send roughly two frames per RTT, bounded by limits on frame rate */ /* Try to send roughly two frames per RTT, bounded by limits on frame rate */
template <class MyState> template<class MyState>
unsigned int TransportSender<MyState>::send_interval( void ) const unsigned int TransportSender<MyState>::send_interval( void ) const
{ {
int SEND_INTERVAL = lrint( ceil( connection->get_SRTT() / 2.0 ) ); int SEND_INTERVAL = lrint( ceil( connection->get_SRTT() / 2.0 ) );
@@ -83,7 +71,7 @@ unsigned int TransportSender<MyState>::send_interval( void ) const
} }
/* Housekeeping routine to calculate next send and ack times */ /* Housekeeping routine to calculate next send and ack times */
template <class MyState> template<class MyState>
void TransportSender<MyState>::calculate_timers( void ) void TransportSender<MyState>::calculate_timers( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -94,38 +82,35 @@ void TransportSender<MyState>::calculate_timers( void )
/* Cut out common prefix of all states */ /* Cut out common prefix of all states */
rationalize_states(); rationalize_states();
if ( pending_data_ack && (next_ack_time > now + ACK_DELAY) ) { if ( pending_data_ack && ( next_ack_time > now + ACK_DELAY ) ) {
next_ack_time = now + ACK_DELAY; next_ack_time = now + ACK_DELAY;
} }
if ( !(current_state == sent_states.back().state) ) { if ( !( current_state == sent_states.back().state ) ) {
if ( mindelay_clock == uint64_t( -1 ) ) { if ( mindelay_clock == uint64_t( -1 ) ) {
mindelay_clock = now; mindelay_clock = now;
} }
next_send_time = std::max( mindelay_clock + SEND_MINDELAY, next_send_time = std::max( mindelay_clock + SEND_MINDELAY, sent_states.back().timestamp + send_interval() );
sent_states.back().timestamp + send_interval() ); } else if ( !( current_state == assumed_receiver_state->state ) && ( last_heard + ACTIVE_RETRY_TIMEOUT > now ) ) {
} else if ( !(current_state == assumed_receiver_state->state)
&& (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) {
next_send_time = sent_states.back().timestamp + send_interval(); next_send_time = sent_states.back().timestamp + send_interval();
if ( mindelay_clock != uint64_t( -1 ) ) { if ( mindelay_clock != uint64_t( -1 ) ) {
next_send_time = std::max( next_send_time, mindelay_clock + SEND_MINDELAY ); next_send_time = std::max( next_send_time, mindelay_clock + SEND_MINDELAY );
} }
} else if ( !(current_state == sent_states.front().state ) } else if ( !( current_state == sent_states.front().state ) && ( last_heard + ACTIVE_RETRY_TIMEOUT > now ) ) {
&& (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) {
next_send_time = sent_states.back().timestamp + connection->timeout() + ACK_DELAY; next_send_time = sent_states.back().timestamp + connection->timeout() + ACK_DELAY;
} else { } else {
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
/* speed up shutdown sequence */ /* speed up shutdown sequence */
if ( shutdown_in_progress || (ack_num == uint64_t(-1)) ) { if ( shutdown_in_progress || ( ack_num == uint64_t( -1 ) ) ) {
next_ack_time = sent_states.back().timestamp + send_interval(); next_ack_time = sent_states.back().timestamp + send_interval();
} }
} }
/* How many ms to wait until next event */ /* How many ms to wait until next event */
template <class MyState> template<class MyState>
int TransportSender<MyState>::wait_time( void ) int TransportSender<MyState>::wait_time( void )
{ {
calculate_timers(); calculate_timers();
@@ -149,7 +134,7 @@ int TransportSender<MyState>::wait_time( void )
} }
/* Send data or an empty ack if necessary */ /* Send data or an empty ack if necessary */
template <class MyState> template<class MyState>
void TransportSender<MyState>::tick( void ) void TransportSender<MyState>::tick( void )
{ {
calculate_timers(); /* updates assumed receiver state and rationalizes */ calculate_timers(); /* updates assumed receiver state and rationalizes */
@@ -160,8 +145,7 @@ void TransportSender<MyState>::tick( void )
uint64_t now = timestamp(); uint64_t now = timestamp();
if ( (now < next_ack_time) if ( ( now < next_ack_time ) && ( now < next_send_time ) ) {
&& (now < next_send_time) ) {
return; return;
} }
@@ -187,22 +171,22 @@ void TransportSender<MyState>::tick( void )
} }
if ( diff.empty() ) { if ( diff.empty() ) {
if ( (now >= next_ack_time) ) { if ( ( now >= next_ack_time ) ) {
send_empty_ack(); send_empty_ack();
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
if ( (now >= next_send_time) ) { if ( ( now >= next_send_time ) ) {
next_send_time = uint64_t( -1 ); next_send_time = uint64_t( -1 );
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
} else if ( (now >= next_send_time) || (now >= next_ack_time) ) { } else if ( ( now >= next_send_time ) || ( now >= next_ack_time ) ) {
/* Send diffs or ack */ /* Send diffs or ack */
send_to_receiver( diff ); send_to_receiver( diff );
mindelay_clock = uint64_t( -1 ); mindelay_clock = uint64_t( -1 );
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_empty_ack( void ) void TransportSender<MyState>::send_empty_ack( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -221,22 +205,24 @@ void TransportSender<MyState>::send_empty_ack( void )
send_in_fragments( "", new_num ); send_in_fragments( "", new_num );
next_ack_time = now + ACK_INTERVAL; next_ack_time = now + ACK_INTERVAL;
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state ) void TransportSender<MyState>::add_sent_state( uint64_t the_timestamp, uint64_t num, MyState& state )
{ {
sent_states.push_back( TimestampedState<MyState>( the_timestamp, num, state ) ); sent_states.push_back( TimestampedState<MyState>( the_timestamp, num, state ) );
if ( sent_states.size() > 32 ) { /* limit on state queue */ if ( sent_states.size() > 32 ) { /* limit on state queue */
typename sent_states_type::iterator last = sent_states.end(); typename sent_states_type::iterator last = sent_states.end();
for ( int i = 0; i < 16; i++ ) { last--; } for ( int i = 0; i < 16; i++ ) {
last--;
}
sent_states.erase( last ); /* erase state from middle of queue */ sent_states.erase( last ); /* erase state from middle of queue */
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_to_receiver( const std::string & diff ) void TransportSender<MyState>::send_to_receiver( const std::string& diff )
{ {
uint64_t new_num; uint64_t new_num;
if ( current_state == sent_states.back().state ) { /* previously sent */ if ( current_state == sent_states.back().state ) { /* previously sent */
@@ -263,10 +249,10 @@ void TransportSender<MyState>::send_to_receiver( const std::string & diff )
assumed_receiver_state = sent_states.end(); assumed_receiver_state = sent_states.end();
assumed_receiver_state--; assumed_receiver_state--;
next_ack_time = timestamp() + ACK_INTERVAL; next_ack_time = timestamp() + ACK_INTERVAL;
next_send_time = uint64_t(-1); next_send_time = uint64_t( -1 );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::update_assumed_receiver_state( void ) void TransportSender<MyState>::update_assumed_receiver_state( void )
{ {
uint64_t now = timestamp(); uint64_t now = timestamp();
@@ -275,13 +261,13 @@ void TransportSender<MyState>::update_assumed_receiver_state( void )
transmitted recently enough ago */ transmitted recently enough ago */
assumed_receiver_state = sent_states.begin(); assumed_receiver_state = sent_states.begin();
typename std::list< TimestampedState<MyState> >::iterator i = sent_states.begin(); typename std::list<TimestampedState<MyState>>::iterator i = sent_states.begin();
i++; i++;
while ( i != sent_states.end() ) { while ( i != sent_states.end() ) {
assert( now >= i->timestamp ); assert( now >= i->timestamp );
if ( uint64_t(now - i->timestamp) < connection->timeout() + ACK_DELAY ) { if ( uint64_t( now - i->timestamp ) < connection->timeout() + ACK_DELAY ) {
assumed_receiver_state = i; assumed_receiver_state = i;
} else { } else {
return; return;
@@ -291,33 +277,33 @@ void TransportSender<MyState>::update_assumed_receiver_state( void )
} }
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::rationalize_states( void ) void TransportSender<MyState>::rationalize_states( void )
{ {
const MyState * known_receiver_state = &sent_states.front().state; const MyState* known_receiver_state = &sent_states.front().state;
current_state.subtract( known_receiver_state ); current_state.subtract( known_receiver_state );
for ( typename std::list< TimestampedState<MyState> >::reverse_iterator i = sent_states.rbegin(); for ( typename std::list<TimestampedState<MyState>>::reverse_iterator i = sent_states.rbegin();
i != sent_states.rend(); i != sent_states.rend();
i++ ) { i++ ) {
i->state.subtract( known_receiver_state ); i->state.subtract( known_receiver_state );
} }
} }
template <class MyState> template<class MyState>
const std::string TransportSender<MyState>::make_chaff( void ) const std::string TransportSender<MyState>::make_chaff( void )
{ {
const size_t CHAFF_MAX = 16; const size_t CHAFF_MAX = 16;
const size_t chaff_len = prng.uint8() % (CHAFF_MAX + 1); const size_t chaff_len = prng.uint8() % ( CHAFF_MAX + 1 );
char chaff[ CHAFF_MAX ]; char chaff[CHAFF_MAX];
prng.fill( chaff, chaff_len ); prng.fill( chaff, chaff_len );
return std::string( chaff, chaff_len ); return std::string( chaff, chaff_len );
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::send_in_fragments( const std::string & diff, uint64_t new_num ) void TransportSender<MyState>::send_in_fragments( const std::string& diff, uint64_t new_num )
{ {
Instruction inst; Instruction inst;
@@ -329,32 +315,37 @@ void TransportSender<MyState>::send_in_fragments( const std::string & diff, uint
inst.set_diff( diff ); inst.set_diff( diff );
inst.set_chaff( make_chaff() ); inst.set_chaff( make_chaff() );
if ( new_num == uint64_t(-1) ) { if ( new_num == uint64_t( -1 ) ) {
shutdown_tries++; shutdown_tries++;
} }
std::vector<Fragment> fragments = fragmenter.make_fragments( inst, connection->get_MTU() std::vector<Fragment> fragments = fragmenter.make_fragments(
- Network::Connection::ADDED_BYTES inst, connection->get_MTU() - Network::Connection::ADDED_BYTES - Crypto::Session::ADDED_BYTES );
- Crypto::Session::ADDED_BYTES ); for ( std::vector<Fragment>::iterator i = fragments.begin(); i != fragments.end(); i++ ) {
for ( std::vector<Fragment>::iterator i = fragments.begin();
i != fragments.end();
i++ ) {
connection->send( i->tostring() ); connection->send( i->tostring() );
if ( verbose ) { if ( verbose ) {
fprintf( stderr, "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n", fprintf(
(unsigned int)(timestamp() % 100000), (int)inst.old_num(), (int)inst.new_num(), (int)i->id, (int)i->fragment_num, stderr,
(int)inst.ack_num(), (int)inst.throwaway_num(), (int)i->contents.size(), "[%u] Sent [%d=>%d] id %d, frag %d ack=%d, throwaway=%d, len=%d, frame rate=%.2f, timeout=%d, srtt=%.1f\n",
1000.0 / (double)send_interval(), (unsigned int)( timestamp() % 100000 ),
(int)connection->timeout(), connection->get_SRTT() ); (int)inst.old_num(),
(int)inst.new_num(),
(int)i->id,
(int)i->fragment_num,
(int)inst.ack_num(),
(int)inst.throwaway_num(),
(int)i->contents.size(),
1000.0 / (double)send_interval(),
(int)connection->timeout(),
connection->get_SRTT() );
} }
} }
pending_data_ack = false; pending_data_ack = false;
} }
template <class MyState> template<class MyState>
void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num ) void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num )
{ {
/* Ignore ack if we have culled the state it's acknowledging */ /* Ignore ack if we have culled the state it's acknowledging */
@@ -371,7 +362,7 @@ void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num
typename sent_states_type::iterator i_next = i; typename sent_states_type::iterator i_next = i;
i_next++; i_next++;
if ( i->num < ack_num ) { if ( i->num < ack_num ) {
sent_states.erase( i ); sent_states.erase( i );
} }
i = i_next; i = i_next;
} }
@@ -380,7 +371,7 @@ void TransportSender<MyState>::process_acknowledgment_through( uint64_t ack_num
} }
/* give up on getting acknowledgement for shutdown */ /* give up on getting acknowledgement for shutdown */
template <class MyState> template<class MyState>
bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const
{ {
if ( shutdown_in_progress ) { if ( shutdown_in_progress ) {
@@ -395,7 +386,7 @@ bool TransportSender<MyState>::shutdown_ack_timed_out( void ) const
} }
/* Executed upon entry to new receiver state */ /* Executed upon entry to new receiver state */
template <class MyState> template<class MyState>
void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num ) void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num )
{ {
ack_num = s_ack_num; ack_num = s_ack_num;
@@ -403,8 +394,8 @@ void TransportSender<MyState>::set_ack_num( uint64_t s_ack_num )
/* Investigate diff against known receiver state instead */ /* Investigate diff against known receiver state instead */
/* Mutates proposed_diff */ /* Mutates proposed_diff */
template <class MyState> template<class MyState>
void TransportSender<MyState>::attempt_prospective_resend_optimization( std::string &proposed_diff ) void TransportSender<MyState>::attempt_prospective_resend_optimization( std::string& proposed_diff )
{ {
if ( assumed_receiver_state == sent_states.begin() ) { if ( assumed_receiver_state == sent_states.begin() ) {
return; return;
@@ -416,9 +407,8 @@ void TransportSender<MyState>::attempt_prospective_resend_optimization( std::str
or if it would lengthen it by no more than 100 bytes and still be or if it would lengthen it by no more than 100 bytes and still be
less than 1000 bytes. */ less than 1000 bytes. */
if ( (resend_diff.size() <= proposed_diff.size()) if ( ( resend_diff.size() <= proposed_diff.size() )
|| ( (resend_diff.size() < 1000) || ( ( resend_diff.size() < 1000 ) && ( resend_diff.size() - proposed_diff.size() < 100 ) ) ) {
&& (resend_diff.size() - proposed_diff.size() < 100) ) ) {
assumed_receiver_state = sent_states.begin(); assumed_receiver_state = sent_states.begin();
proposed_diff = resend_diff; proposed_diff = resend_diff;
} }
+121 -112
View File
@@ -30,137 +30,146 @@
also delete it here. also delete it here.
*/ */
#ifndef TRANSPORT_SENDER_HPP #ifndef TRANSPORT_SENDER_HPP
#define TRANSPORT_SENDER_HPP #define TRANSPORT_SENDER_HPP
#include <string>
#include <list> #include <list>
#include <string>
#include "src/crypto/prng.h"
#include "src/network/network.h" #include "src/network/network.h"
#include "src/protobufs/transportinstruction.pb.h" #include "src/protobufs/transportinstruction.pb.h"
#include "transportstate.h"
#include "transportfragment.h" #include "transportfragment.h"
#include "src/crypto/prng.h" #include "transportstate.h"
namespace Network { namespace Network {
using namespace TransportBuffers; using namespace TransportBuffers;
/* timing parameters */ /* timing parameters */
const int SEND_INTERVAL_MIN = 20; /* ms between frames */ const int SEND_INTERVAL_MIN = 20; /* ms between frames */
const int SEND_INTERVAL_MAX = 250; /* ms between frames */ const int SEND_INTERVAL_MAX = 250; /* ms between frames */
const int ACK_INTERVAL = 3000; /* ms between empty acks */ const int ACK_INTERVAL = 3000; /* ms between empty acks */
const int ACK_DELAY = 100; /* ms before delayed ack */ const int ACK_DELAY = 100; /* ms before delayed ack */
const int SHUTDOWN_RETRIES = 16; /* number of shutdown packets to send before giving up */ const int SHUTDOWN_RETRIES = 16; /* number of shutdown packets to send before giving up */
const int ACTIVE_RETRY_TIMEOUT = 10000; /* attempt to resend at frame rate */ const int ACTIVE_RETRY_TIMEOUT = 10000; /* attempt to resend at frame rate */
template <class MyState> template<class MyState>
class TransportSender class TransportSender
{
private:
/* helper methods for tick() */
void update_assumed_receiver_state( void );
void attempt_prospective_resend_optimization( std::string& proposed_diff );
void rationalize_states( void );
void send_to_receiver( const std::string& diff );
void send_empty_ack( void );
void send_in_fragments( const std::string& diff, uint64_t new_num );
void add_sent_state( uint64_t the_timestamp, uint64_t num, MyState& state );
/* state of sender */
Connection* connection;
MyState current_state;
using sent_states_type = std::list<TimestampedState<MyState>>;
sent_states_type sent_states;
/* first element: known, acknowledged receiver state */
/* last element: last sent state */
/* somewhere in the middle: the assumed state of the receiver */
typename sent_states_type::iterator assumed_receiver_state;
/* for fragment creation */
Fragmenter fragmenter;
/* timing state */
uint64_t next_ack_time;
uint64_t next_send_time;
void calculate_timers( void );
unsigned int verbose;
bool shutdown_in_progress;
int shutdown_tries;
uint64_t shutdown_start;
/* information about receiver state */
uint64_t ack_num;
bool pending_data_ack;
unsigned int SEND_MINDELAY; /* ms to collect all input */
uint64_t last_heard; /* last time received new state */
/* chaff to disguise instruction length */
PRNG prng;
const std::string make_chaff( void );
uint64_t mindelay_clock; /* time of first pending change to current state */
public:
/* constructor */
TransportSender( Connection* s_connection, MyState& initial_state );
/* Send data or an ack if necessary */
void tick( void );
/* Returns the number of ms to wait until next possible event. */
int wait_time( void );
/* Executed upon receipt of ack */
void process_acknowledgment_through( uint64_t ack_num );
/* Executed upon entry to new receiver state */
void set_ack_num( uint64_t s_ack_num );
/* Accelerate reply ack */
void set_data_ack( void ) { pending_data_ack = true; }
/* Received something */
void remote_heard( uint64_t ts ) { last_heard = ts; }
/* Starts shutdown sequence */
void start_shutdown( void )
{ {
private: if ( !shutdown_in_progress ) {
/* helper methods for tick() */ shutdown_start = timestamp();
void update_assumed_receiver_state( void ); shutdown_in_progress = true;
void attempt_prospective_resend_optimization( std::string &proposed_diff );
void rationalize_states( void );
void send_to_receiver( const std::string & diff );
void send_empty_ack( void );
void send_in_fragments( const std::string & diff, uint64_t new_num );
void add_sent_state( uint64_t the_timestamp, uint64_t num, MyState &state );
/* state of sender */
Connection *connection;
MyState current_state;
using sent_states_type = std::list<TimestampedState<MyState>>;
sent_states_type sent_states;
/* first element: known, acknowledged receiver state */
/* last element: last sent state */
/* somewhere in the middle: the assumed state of the receiver */
typename sent_states_type::iterator assumed_receiver_state;
/* for fragment creation */
Fragmenter fragmenter;
/* timing state */
uint64_t next_ack_time;
uint64_t next_send_time;
void calculate_timers( void );
unsigned int verbose;
bool shutdown_in_progress;
int shutdown_tries;
uint64_t shutdown_start;
/* information about receiver state */
uint64_t ack_num;
bool pending_data_ack;
unsigned int SEND_MINDELAY; /* ms to collect all input */
uint64_t last_heard; /* last time received new state */
/* chaff to disguise instruction length */
PRNG prng;
const std::string make_chaff( void );
uint64_t mindelay_clock; /* time of first pending change to current state */
public:
/* constructor */
TransportSender( Connection *s_connection, MyState &initial_state );
/* Send data or an ack if necessary */
void tick( void );
/* Returns the number of ms to wait until next possible event. */
int wait_time( void );
/* Executed upon receipt of ack */
void process_acknowledgment_through( uint64_t ack_num );
/* Executed upon entry to new receiver state */
void set_ack_num( uint64_t s_ack_num );
/* Accelerate reply ack */
void set_data_ack( void ) { pending_data_ack = true; }
/* Received something */
void remote_heard( uint64_t ts ) { last_heard = ts; }
/* Starts shutdown sequence */
void start_shutdown( void ) { if ( !shutdown_in_progress ) { shutdown_start = timestamp(); shutdown_in_progress = true; } }
/* Misc. getters and setters */
/* Cannot modify current_state while shutdown in progress */
MyState &get_current_state( void ) { assert( !shutdown_in_progress ); return current_state; }
void set_current_state( const MyState &x )
{
assert( !shutdown_in_progress );
current_state = x;
current_state.reset_input();
} }
void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; } }
bool get_shutdown_in_progress( void ) const { return shutdown_in_progress; } /* Misc. getters and setters */
bool get_shutdown_acknowledged( void ) const { return sent_states.front().num == uint64_t(-1); } /* Cannot modify current_state while shutdown in progress */
bool get_counterparty_shutdown_acknowledged( void ) const { return fragmenter.last_ack_sent() == uint64_t(-1); } MyState& get_current_state( void )
uint64_t get_sent_state_acked_timestamp( void ) const { return sent_states.front().timestamp; } {
uint64_t get_sent_state_acked( void ) const { return sent_states.front().num; } assert( !shutdown_in_progress );
uint64_t get_sent_state_last( void ) const { return sent_states.back().num; } return current_state;
}
void set_current_state( const MyState& x )
{
assert( !shutdown_in_progress );
current_state = x;
current_state.reset_input();
}
void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; }
bool shutdown_ack_timed_out( void ) const; bool get_shutdown_in_progress( void ) const { return shutdown_in_progress; }
bool get_shutdown_acknowledged( void ) const { return sent_states.front().num == uint64_t( -1 ); }
bool get_counterparty_shutdown_acknowledged( void ) const { return fragmenter.last_ack_sent() == uint64_t( -1 ); }
uint64_t get_sent_state_acked_timestamp( void ) const { return sent_states.front().timestamp; }
uint64_t get_sent_state_acked( void ) const { return sent_states.front().num; }
uint64_t get_sent_state_last( void ) const { return sent_states.back().num; }
void set_send_delay( int new_delay ) { SEND_MINDELAY = new_delay; } bool shutdown_ack_timed_out( void ) const;
unsigned int send_interval( void ) const; void set_send_delay( int new_delay ) { SEND_MINDELAY = new_delay; }
/* nonexistent methods to satisfy -Weffc++ */ unsigned int send_interval( void ) const;
TransportSender( const TransportSender &x );
TransportSender & operator=( const TransportSender &x ); /* nonexistent methods to satisfy -Weffc++ */
}; TransportSender( const TransportSender& x );
TransportSender& operator=( const TransportSender& x );
};
} }
#endif #endif
+11 -11
View File
@@ -34,18 +34,18 @@
#define TRANSPORT_STATE_HPP #define TRANSPORT_STATE_HPP
namespace Network { namespace Network {
template <class State> template<class State>
class TimestampedState class TimestampedState
{ {
public: public:
uint64_t timestamp; uint64_t timestamp;
uint64_t num; uint64_t num;
State state; State state;
TimestampedState( uint64_t s_timestamp, uint64_t s_num, const State &s_state ) TimestampedState( uint64_t s_timestamp, uint64_t s_num, const State& s_state )
: timestamp( s_timestamp ), num( s_num ), state( s_state ) : timestamp( s_timestamp ), num( s_num ), state( s_state )
{} {}
}; };
} }
#endif #endif
+32 -34
View File
@@ -41,17 +41,15 @@ using namespace Parser;
using namespace Terminal; using namespace Terminal;
using namespace HostBuffers; using namespace HostBuffers;
string Complete::act( const string &str ) string Complete::act( const string& str )
{ {
for ( unsigned int i = 0; i < str.size(); i++ ) { for ( unsigned int i = 0; i < str.size(); i++ ) {
/* parse octet into up to three actions */ /* parse octet into up to three actions */
parser.input( str[ i ], actions ); parser.input( str[i], actions );
/* apply actions to terminal and delete them */ /* apply actions to terminal and delete them */
for ( Actions::iterator it = actions.begin(); for ( Actions::iterator it = actions.begin(); it != actions.end(); it++ ) {
it != actions.end(); Action& act = **it;
it++ ) {
Action &act = **it;
act.act_on_terminal( &terminal ); act.act_on_terminal( &terminal );
} }
actions.clear(); actions.clear();
@@ -60,7 +58,7 @@ string Complete::act( const string &str )
return terminal.read_octets_to_host(); return terminal.read_octets_to_host();
} }
string Complete::act( const Action &act ) string Complete::act( const Action& act )
{ {
/* apply action to terminal */ /* apply action to terminal */
act.act_on_terminal( &terminal ); act.act_on_terminal( &terminal );
@@ -68,26 +66,26 @@ string Complete::act( const Action &act )
} }
/* interface for Network::Transport */ /* interface for Network::Transport */
string Complete::diff_from( const Complete &existing ) const string Complete::diff_from( const Complete& existing ) const
{ {
HostBuffers::HostMessage output; HostBuffers::HostMessage output;
if ( existing.get_echo_ack() != get_echo_ack() ) { if ( existing.get_echo_ack() != get_echo_ack() ) {
assert( get_echo_ack() >= existing.get_echo_ack() ); assert( get_echo_ack() >= existing.get_echo_ack() );
Instruction *new_echo = output.add_instruction(); Instruction* new_echo = output.add_instruction();
new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() ); new_echo->MutableExtension( echoack )->set_echo_ack_num( get_echo_ack() );
} }
if ( !(existing.get_fb() == get_fb()) ) { if ( !( existing.get_fb() == get_fb() ) ) {
if ( (existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width()) if ( ( existing.get_fb().ds.get_width() != terminal.get_fb().ds.get_width() )
|| (existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height()) ) { || ( existing.get_fb().ds.get_height() != terminal.get_fb().ds.get_height() ) ) {
Instruction *new_res = output.add_instruction(); Instruction* new_res = output.add_instruction();
new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() ); new_res->MutableExtension( resize )->set_width( terminal.get_fb().ds.get_width() );
new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() ); new_res->MutableExtension( resize )->set_height( terminal.get_fb().ds.get_height() );
} }
string update = display.new_frame( true, existing.get_fb(), terminal.get_fb() ); string update = display.new_frame( true, existing.get_fb(), terminal.get_fb() );
if ( !update.empty() ) { if ( !update.empty() ) {
Instruction *new_inst = output.add_instruction(); Instruction* new_inst = output.add_instruction();
new_inst->MutableExtension( hostbytes )->set_hoststring( update ); new_inst->MutableExtension( hostbytes )->set_hoststring( update );
} }
} }
@@ -97,10 +95,10 @@ string Complete::diff_from( const Complete &existing ) const
string Complete::init_diff( void ) const string Complete::init_diff( void ) const
{ {
return diff_from( Complete( get_fb().ds.get_width(), get_fb().ds.get_height() )); return diff_from( Complete( get_fb().ds.get_width(), get_fb().ds.get_height() ) );
} }
void Complete::apply_string( const string & diff ) void Complete::apply_string( const string& diff )
{ {
HostBuffers::HostMessage input; HostBuffers::HostMessage input;
fatal_assert( input.ParseFromString( diff ) ); fatal_assert( input.ParseFromString( diff ) );
@@ -111,7 +109,7 @@ void Complete::apply_string( const string & diff )
assert( terminal_to_host.empty() ); /* server never interrogates client terminal */ assert( terminal_to_host.empty() ); /* server never interrogates client terminal */
} else if ( input.instruction( i ).HasExtension( resize ) ) { } else if ( input.instruction( i ).HasExtension( resize ) ) {
act( Resize( input.instruction( i ).GetExtension( resize ).width(), act( Resize( input.instruction( i ).GetExtension( resize ).width(),
input.instruction( i ).GetExtension( resize ).height() ) ); input.instruction( i ).GetExtension( resize ).height() ) );
} else if ( input.instruction( i ).HasExtension( echoack ) ) { } else if ( input.instruction( i ).HasExtension( echoack ) ) {
uint64_t inst_echo_ack_num = input.instruction( i ).GetExtension( echoack ).echo_ack_num(); uint64_t inst_echo_ack_num = input.instruction( i ).GetExtension( echoack ).echo_ack_num();
assert( inst_echo_ack_num >= echo_ack ); assert( inst_echo_ack_num >= echo_ack );
@@ -120,10 +118,10 @@ void Complete::apply_string( const string & diff )
} }
} }
bool Complete::operator==( Complete const &x ) const bool Complete::operator==( Complete const& x ) const
{ {
// assert( parser == x.parser ); /* parser state is irrelevant for us */ // assert( parser == x.parser ); /* parser state is irrelevant for us */
return (terminal == x.terminal) && (echo_ack == x.echo_ack); return ( terminal == x.terminal ) && ( echo_ack == x.echo_ack );
} }
bool Complete::set_echo_ack( uint64_t now ) bool Complete::set_echo_ack( uint64_t now )
@@ -131,16 +129,13 @@ bool Complete::set_echo_ack( uint64_t now )
bool ret = false; bool ret = false;
uint64_t newest_echo_ack = 0; uint64_t newest_echo_ack = 0;
for ( input_history_type::const_iterator i = input_history.begin(); for ( input_history_type::const_iterator i = input_history.begin(); i != input_history.end(); i++ ) {
i != input_history.end();
i++ ) {
if ( i->second <= now - ECHO_TIMEOUT ) { if ( i->second <= now - ECHO_TIMEOUT ) {
newest_echo_ack = i->first; newest_echo_ack = i->first;
} }
} }
for ( input_history_type::iterator i = input_history.begin(); for ( input_history_type::iterator i = input_history.begin(); i != input_history.end(); ) {
i != input_history.end(); ) {
input_history_type::iterator i_next = i; input_history_type::iterator i_next = i;
i_next++; i_next++;
if ( i->first < newest_echo_ack ) { if ( i->first < newest_echo_ack ) {
@@ -179,11 +174,11 @@ int Complete::wait_time( uint64_t now ) const
return next_echo_ack_time - now; return next_echo_ack_time - now;
} }
bool Complete::compare( const Complete &other ) const bool Complete::compare( const Complete& other ) const
{ {
bool ret = false; bool ret = false;
const Framebuffer &fb = terminal.get_fb(); const Framebuffer& fb = terminal.get_fb();
const Framebuffer &other_fb = other.terminal.get_fb(); const Framebuffer& other_fb = other.terminal.get_fb();
const int height = fb.ds.get_height(); const int height = fb.ds.get_height();
const int other_height = other_fb.ds.get_height(); const int other_height = other_fb.ds.get_height();
const int width = fb.ds.get_width(); const int width = fb.ds.get_width();
@@ -197,17 +192,20 @@ bool Complete::compare( const Complete &other ) const
for ( int y = 0; y < height; y++ ) { for ( int y = 0; y < height; y++ ) {
for ( int x = 0; x < width; x++ ) { for ( int x = 0; x < width; x++ ) {
if ( fb.get_cell( y, x )->compare( *other_fb.get_cell( y, x ) ) ) { if ( fb.get_cell( y, x )->compare( *other_fb.get_cell( y, x ) ) ) {
fprintf( stderr, "Cell (%d, %d) differs.\n", y, x ); fprintf( stderr, "Cell (%d, %d) differs.\n", y, x );
ret = true; ret = true;
} }
} }
} }
if ( (fb.ds.get_cursor_row() != other_fb.ds.get_cursor_row()) if ( ( fb.ds.get_cursor_row() != other_fb.ds.get_cursor_row() )
|| (fb.ds.get_cursor_col() != other_fb.ds.get_cursor_col()) ) { || ( fb.ds.get_cursor_col() != other_fb.ds.get_cursor_col() ) ) {
fprintf( stderr, "Cursor mismatch: (%d, %d) vs. (%d, %d).\n", fprintf( stderr,
fb.ds.get_cursor_row(), fb.ds.get_cursor_col(), "Cursor mismatch: (%d, %d) vs. (%d, %d).\n",
other_fb.ds.get_cursor_row(), other_fb.ds.get_cursor_col() ); fb.ds.get_cursor_row(),
fb.ds.get_cursor_col(),
other_fb.ds.get_cursor_row(),
other_fb.ds.get_cursor_col() );
ret = true; ret = true;
} }
/* XXX should compare other terminal state too (mouse mode, bell. etc.) */ /* XXX should compare other terminal state too (mouse mode, bell. etc.) */
+34 -32
View File
@@ -42,46 +42,48 @@
/* This class represents the complete terminal -- a UTF8Parser feeding Actions to an Emulator. */ /* This class represents the complete terminal -- a UTF8Parser feeding Actions to an Emulator. */
namespace Terminal { namespace Terminal {
class Complete { class Complete
private: {
Parser::UTF8Parser parser; private:
Terminal::Emulator terminal; Parser::UTF8Parser parser;
Terminal::Display display; Terminal::Emulator terminal;
Terminal::Display display;
// Only used locally by act(), but kept here as a performance optimization, // Only used locally by act(), but kept here as a performance optimization,
// to avoid construction/destruction. It must always be empty // to avoid construction/destruction. It must always be empty
// outside calls to act() to keep horrible things from happening. // outside calls to act() to keep horrible things from happening.
Parser::Actions actions; Parser::Actions actions;
using input_history_type = std::list<std::pair<uint64_t, uint64_t>>; using input_history_type = std::list<std::pair<uint64_t, uint64_t>>;
input_history_type input_history; input_history_type input_history;
uint64_t echo_ack; uint64_t echo_ack;
static const int ECHO_TIMEOUT = 50; /* for late ack */ static const int ECHO_TIMEOUT = 50; /* for late ack */
public: public:
Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ), Complete( size_t width, size_t height )
actions(), input_history(), echo_ack( 0 ) {} : parser(), terminal( width, height ), display( false ), actions(), input_history(), echo_ack( 0 )
{}
std::string act( const std::string &str ); std::string act( const std::string& str );
std::string act( const Parser::Action &act ); std::string act( const Parser::Action& act );
const Framebuffer & get_fb( void ) const { return terminal.get_fb(); } const Framebuffer& get_fb( void ) const { return terminal.get_fb(); }
void reset_input( void ) { parser.reset_input(); } void reset_input( void ) { parser.reset_input(); }
uint64_t get_echo_ack( void ) const { return echo_ack; } uint64_t get_echo_ack( void ) const { return echo_ack; }
bool set_echo_ack( uint64_t now ); bool set_echo_ack( uint64_t now );
void register_input_frame( uint64_t n, uint64_t now ); void register_input_frame( uint64_t n, uint64_t now );
int wait_time( uint64_t now ) const; int wait_time( uint64_t now ) const;
/* interface for Network::Transport */ /* interface for Network::Transport */
void subtract( const Complete * ) const {} void subtract( const Complete* ) const {}
std::string diff_from( const Complete &existing ) const; std::string diff_from( const Complete& existing ) const;
std::string init_diff( void ) const; std::string init_diff( void ) const;
void apply_string( const std::string & diff ); void apply_string( const std::string& diff );
bool operator==( const Complete &x ) const; bool operator==( const Complete& x ) const;
bool compare( const Complete &other ) const; bool compare( const Complete& other ) const;
}; };
} }
#endif #endif
+40 -45
View File
@@ -33,24 +33,22 @@
#include <cassert> #include <cassert>
#include <typeinfo> #include <typeinfo>
#include "src/protobufs/userinput.pb.h"
#include "src/statesync/user.h" #include "src/statesync/user.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "src/protobufs/userinput.pb.h"
using namespace Parser; using namespace Parser;
using namespace Network; using namespace Network;
using namespace ClientBuffers; using namespace ClientBuffers;
void UserStream::subtract( const UserStream *prefix ) void UserStream::subtract( const UserStream* prefix )
{ {
// if we are subtracting ourself from ourself, just clear the std::deque // if we are subtracting ourself from ourself, just clear the std::deque
if ( this == prefix ) { if ( this == prefix ) {
actions.clear(); actions.clear();
return; return;
} }
for ( std::deque<UserEvent>::const_iterator i = prefix->actions.begin(); for ( std::deque<UserEvent>::const_iterator i = prefix->actions.begin(); i != prefix->actions.end(); i++ ) {
i != prefix->actions.end();
i++ ) {
assert( this != prefix ); assert( this != prefix );
assert( !actions.empty() ); assert( !actions.empty() );
assert( *i == actions.front() ); assert( *i == actions.front() );
@@ -58,13 +56,11 @@ void UserStream::subtract( const UserStream *prefix )
} }
} }
std::string UserStream::diff_from( const UserStream &existing ) const std::string UserStream::diff_from( const UserStream& existing ) const
{ {
std::deque<UserEvent>::const_iterator my_it = actions.begin(); std::deque<UserEvent>::const_iterator my_it = actions.begin();
for ( std::deque<UserEvent>::const_iterator i = existing.actions.begin(); for ( std::deque<UserEvent>::const_iterator i = existing.actions.begin(); i != existing.actions.end(); i++ ) {
i != existing.actions.end();
i++ ) {
assert( my_it != actions.end() ); assert( my_it != actions.end() );
assert( *i == *my_it ); assert( *i == *my_it );
my_it++; my_it++;
@@ -74,29 +70,28 @@ std::string UserStream::diff_from( const UserStream &existing ) const
while ( my_it != actions.end() ) { while ( my_it != actions.end() ) {
switch ( my_it->type ) { switch ( my_it->type ) {
case UserByteType: case UserByteType: {
{ char the_byte = my_it->userbyte.c;
char the_byte = my_it->userbyte.c; /* can we combine this with a previous Keystroke? */
/* can we combine this with a previous Keystroke? */ if ( ( output.instruction_size() > 0 )
if ( (output.instruction_size() > 0) && ( output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke ) ) ) {
&& (output.instruction( output.instruction_size() - 1 ).HasExtension( keystroke )) ) { output.mutable_instruction( output.instruction_size() - 1 )
output.mutable_instruction( output.instruction_size() - 1 )->MutableExtension( keystroke )->mutable_keys()->append( std::string( &the_byte, 1 ) ); ->MutableExtension( keystroke )
} else { ->mutable_keys()
Instruction *new_inst = output.add_instruction(); ->append( std::string( &the_byte, 1 ) );
new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 ); } else {
} Instruction* new_inst = output.add_instruction();
} new_inst->MutableExtension( keystroke )->set_keys( &the_byte, 1 );
break; }
case ResizeType: } break;
{ case ResizeType: {
Instruction *new_inst = output.add_instruction(); Instruction* new_inst = output.add_instruction();
new_inst->MutableExtension( resize )->set_width( my_it->resize.width ); new_inst->MutableExtension( resize )->set_width( my_it->resize.width );
new_inst->MutableExtension( resize )->set_height( my_it->resize.height ); new_inst->MutableExtension( resize )->set_height( my_it->resize.height );
} } break;
break; default:
default: assert( !"unexpected event type" );
assert( !"unexpected event type" ); break;
break;
} }
my_it++; my_it++;
@@ -105,7 +100,7 @@ std::string UserStream::diff_from( const UserStream &existing ) const
return output.SerializeAsString(); return output.SerializeAsString();
} }
void UserStream::apply_string( const std::string &diff ) void UserStream::apply_string( const std::string& diff )
{ {
ClientBuffers::UserMessage input; ClientBuffers::UserMessage input;
fatal_assert( input.ParseFromString( diff ) ); fatal_assert( input.ParseFromString( diff ) );
@@ -114,25 +109,25 @@ void UserStream::apply_string( const std::string &diff )
if ( input.instruction( i ).HasExtension( keystroke ) ) { if ( input.instruction( i ).HasExtension( keystroke ) ) {
std::string the_bytes = input.instruction( i ).GetExtension( keystroke ).keys(); std::string the_bytes = input.instruction( i ).GetExtension( keystroke ).keys();
for ( unsigned int loc = 0; loc < the_bytes.size(); loc++ ) { for ( unsigned int loc = 0; loc < the_bytes.size(); loc++ ) {
actions.push_back( UserEvent( UserByte( the_bytes.at( loc ) ) ) ); actions.push_back( UserEvent( UserByte( the_bytes.at( loc ) ) ) );
} }
} else if ( input.instruction( i ).HasExtension( resize ) ) { } else if ( input.instruction( i ).HasExtension( resize ) ) {
actions.push_back( UserEvent( Resize( input.instruction( i ).GetExtension( resize ).width(), actions.push_back( UserEvent( Resize( input.instruction( i ).GetExtension( resize ).width(),
input.instruction( i ).GetExtension( resize ).height() ) ) ); input.instruction( i ).GetExtension( resize ).height() ) ) );
} }
} }
} }
const Parser::Action &UserStream::get_action( unsigned int i ) const const Parser::Action& UserStream::get_action( unsigned int i ) const
{ {
switch( actions[ i ].type ) { switch ( actions[i].type ) {
case UserByteType: case UserByteType:
return actions[ i ].userbyte; return actions[i].userbyte;
case ResizeType: case ResizeType:
return actions[ i ].resize; return actions[i].resize;
default: default:
assert( !"unexpected action type" ); assert( !"unexpected action type" );
static const Parser::Ignore nothing = Parser::Ignore(); static const Parser::Ignore nothing = Parser::Ignore();
return nothing; return nothing;
} }
} }
+43 -38
View File
@@ -41,52 +41,57 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
namespace Network { namespace Network {
enum UserEventType { enum UserEventType
UserByteType = 0, {
ResizeType = 1 UserByteType = 0,
}; ResizeType = 1
};
class UserEvent class UserEvent
{
public:
UserEventType type;
Parser::UserByte userbyte;
Parser::Resize resize;
UserEvent( const Parser::UserByte& s_userbyte ) : type( UserByteType ), userbyte( s_userbyte ), resize( -1, -1 )
{}
UserEvent( const Parser::Resize& s_resize ) : type( ResizeType ), userbyte( 0 ), resize( s_resize ) {}
private:
UserEvent();
public:
bool operator==( const UserEvent& x ) const
{ {
public: return ( type == x.type ) && ( userbyte == x.userbyte ) && ( resize == x.resize );
UserEventType type; }
Parser::UserByte userbyte; };
Parser::Resize resize;
UserEvent( const Parser::UserByte & s_userbyte ) : type( UserByteType ), userbyte( s_userbyte ), resize( -1, -1 ) {} class UserStream
UserEvent( const Parser::Resize & s_resize ) : type( ResizeType ), userbyte( 0 ), resize( s_resize ) {} {
private:
std::deque<UserEvent> actions;
private: public:
UserEvent(); UserStream() : actions() {}
public: void push_back( const Parser::UserByte& s_userbyte ) { actions.push_back( UserEvent( s_userbyte ) ); }
bool operator==( const UserEvent &x ) const { return ( type == x.type ) && ( userbyte == x.userbyte ) && ( resize == x.resize ); } void push_back( const Parser::Resize& s_resize ) { actions.push_back( UserEvent( s_resize ) ); }
};
class UserStream bool empty( void ) const { return actions.empty(); }
{ size_t size( void ) const { return actions.size(); }
private: const Parser::Action& get_action( unsigned int i ) const;
std::deque<UserEvent> actions;
public: /* interface for Network::Transport */
UserStream() : actions() {} void subtract( const UserStream* prefix );
std::string diff_from( const UserStream& existing ) const;
std::string init_diff( void ) const { return diff_from( UserStream() ); };
void apply_string( const std::string& diff );
bool operator==( const UserStream& x ) const { return actions == x.actions; }
void push_back( const Parser::UserByte & s_userbyte ) { actions.push_back( UserEvent( s_userbyte ) ); } bool compare( const UserStream& ) { return false; }
void push_back( const Parser::Resize & s_resize ) { actions.push_back( UserEvent( s_resize ) ); } };
bool empty( void ) const { return actions.empty(); }
size_t size( void ) const { return actions.size(); }
const Parser::Action &get_action( unsigned int i ) const;
/* interface for Network::Transport */
void subtract( const UserStream *prefix );
std::string diff_from( const UserStream &existing ) const;
std::string init_diff( void ) const { return diff_from( UserStream() ); };
void apply_string( const std::string &diff );
bool operator==( const UserStream &x ) const { return actions == x.actions; }
bool compare( const UserStream & ) { return false; }
};
} }
#endif #endif
+23 -27
View File
@@ -40,8 +40,7 @@
const Parser::StateFamily Parser::family; const Parser::StateFamily Parser::family;
static void append_or_delete( Parser::ActionPointer act, static void append_or_delete( Parser::ActionPointer act, Parser::Actions& vec )
Parser::Actions &vec )
{ {
assert( act ); assert( act );
@@ -50,7 +49,7 @@ static void append_or_delete( Parser::ActionPointer act,
} }
} }
void Parser::Parser::input( wchar_t ch, Actions &ret ) void Parser::Parser::input( wchar_t ch, Actions& ret )
{ {
Transition tx = state->input( ch ); Transition tx = state->input( ch );
@@ -66,24 +65,23 @@ void Parser::Parser::input( wchar_t ch, Actions &ret )
} }
} }
Parser::UTF8Parser::UTF8Parser() Parser::UTF8Parser::UTF8Parser() : parser(), buf_len( 0 )
: parser(), buf_len( 0 )
{ {
assert( BUF_SIZE >= (size_t)MB_CUR_MAX ); assert( BUF_SIZE >= (size_t)MB_CUR_MAX );
buf[0] = '\0'; buf[0] = '\0';
} }
void Parser::UTF8Parser::input( char c, Actions &ret ) void Parser::UTF8Parser::input( char c, Actions& ret )
{ {
assert( buf_len < BUF_SIZE ); assert( buf_len < BUF_SIZE );
/* 1-byte UTF-8 character, aka ASCII? Cheat. */ /* 1-byte UTF-8 character, aka ASCII? Cheat. */
if ( buf_len == 0 && static_cast<unsigned char>(c) <= 0x7f ) { if ( buf_len == 0 && static_cast<unsigned char>( c ) <= 0x7f ) {
parser.input( static_cast<wchar_t>(c), ret ); parser.input( static_cast<wchar_t>( c ), ret );
return; return;
} }
buf[ buf_len++ ] = c; buf[buf_len++] = c;
/* This function will only work in a UTF-8 locale. */ /* This function will only work in a UTF-8 locale. */
wchar_t pwc; wchar_t pwc;
@@ -108,19 +106,19 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
buf_len = 0; buf_len = 0;
pwc = L'\0'; pwc = L'\0';
bytes_parsed = 1; bytes_parsed = 1;
} else if ( bytes_parsed == (size_t) -1 ) { } else if ( bytes_parsed == (size_t)-1 ) {
/* invalid sequence, use replacement character and try again with last char */ /* invalid sequence, use replacement character and try again with last char */
assert( errno == EILSEQ ); assert( errno == EILSEQ );
if ( buf_len > 1 ) { if ( buf_len > 1 ) {
buf[ 0 ] = buf[ buf_len - 1 ]; buf[0] = buf[buf_len - 1];
bytes_parsed = buf_len - 1; bytes_parsed = buf_len - 1;
buf_len = 1; buf_len = 1;
} else { } else {
buf_len = 0; buf_len = 0;
bytes_parsed = 1; bytes_parsed = 1;
} }
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} else if ( bytes_parsed == (size_t) -2 ) { } else if ( bytes_parsed == (size_t)-2 ) {
/* can't parse incomplete multibyte character */ /* can't parse incomplete multibyte character */
total_bytes_parsed += buf_len; total_bytes_parsed += buf_len;
continue; continue;
@@ -137,16 +135,16 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
const uint32_t pwcheck = pwc; const uint32_t pwcheck = pwc;
if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */ if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} }
if ( (pwcheck >= 0xD800) && (pwcheck <= 0xDFFF) ) { /* surrogate code point */ if ( ( pwcheck >= 0xD800 ) && ( pwcheck <= 0xDFFF ) ) { /* surrogate code point */
/* /*
OS X unfortunately allows these sequences without EILSEQ, but OS X unfortunately allows these sequences without EILSEQ, but
they are ill-formed UTF-8 and we shouldn't repeat them to the they are ill-formed UTF-8 and we shouldn't repeat them to the
user's terminal. user's terminal.
*/ */
pwc = (wchar_t) 0xFFFD; pwc = (wchar_t)0xFFFD;
} }
parser.input( pwc, ret ); parser.input( pwc, ret );
@@ -155,11 +153,9 @@ void Parser::UTF8Parser::input( char c, Actions &ret )
} }
} }
Parser::Parser::Parser( const Parser &other ) Parser::Parser::Parser( const Parser& other ) : state( other.state ) {}
: state( other.state )
{}
Parser::Parser & Parser::Parser::operator=( const Parser &other ) Parser::Parser& Parser::Parser::operator=( const Parser& other )
{ {
state = other.state; state = other.state;
return *this; return *this;
+32 -34
View File
@@ -39,55 +39,53 @@
#include <cstring> #include <cstring>
#include <cwchar> #include <cwchar>
#include "parsertransition.h"
#include "src/terminal/parseraction.h"
#include "parserstate.h" #include "parserstate.h"
#include "parserstatefamily.h" #include "parserstatefamily.h"
#include "parsertransition.h"
#include "src/terminal/parseraction.h"
namespace Parser { namespace Parser {
extern const StateFamily family; extern const StateFamily family;
class Parser { class Parser
private: {
State const *state; private:
State const* state;
public: public:
Parser() : state( &family.s_Ground ) {} Parser() : state( &family.s_Ground ) {}
Parser( const Parser &other ); Parser( const Parser& other );
Parser & operator=( const Parser & ); Parser& operator=( const Parser& );
~Parser() {} ~Parser() {}
void input( wchar_t ch, Actions &actions ); void input( wchar_t ch, Actions& actions );
void reset_input( void ) void reset_input( void ) { state = &family.s_Ground; }
{ };
state = &family.s_Ground;
}
}; static const size_t BUF_SIZE = 8;
static const size_t BUF_SIZE = 8; class UTF8Parser
{
private:
Parser parser;
class UTF8Parser { char buf[BUF_SIZE];
private: size_t buf_len;
Parser parser;
char buf[ BUF_SIZE ]; public:
size_t buf_len; UTF8Parser();
public: void input( char c, Actions& actions );
UTF8Parser();
void input( char c, Actions &actions ); void reset_input( void )
{
void reset_input( void ) parser.reset_input();
{ buf[0] = '\0';
parser.reset_input(); buf_len = 0;
buf[0] = '\0'; }
buf_len = 0; };
}
};
} }
#endif #endif
+13 -14
View File
@@ -38,63 +38,62 @@
using namespace Parser; using namespace Parser;
void Print::act_on_terminal( Terminal::Emulator *emu ) const void Print::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->print( this ); emu->print( this );
} }
void Execute::act_on_terminal( Terminal::Emulator *emu ) const void Execute::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->execute( this ); emu->execute( this );
} }
void Clear::act_on_terminal( Terminal::Emulator *emu ) const void Clear::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.clear( this ); emu->dispatch.clear( this );
} }
void Param::act_on_terminal( Terminal::Emulator *emu ) const void Param::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.newparamchar( this ); emu->dispatch.newparamchar( this );
} }
void Collect::act_on_terminal( Terminal::Emulator *emu ) const void Collect::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.collect( this ); emu->dispatch.collect( this );
} }
void CSI_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const void CSI_Dispatch::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->CSI_dispatch( this ); emu->CSI_dispatch( this );
} }
void Esc_Dispatch::act_on_terminal( Terminal::Emulator *emu ) const void Esc_Dispatch::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->Esc_dispatch( this ); emu->Esc_dispatch( this );
} }
void OSC_Put::act_on_terminal( Terminal::Emulator *emu ) const void OSC_Put::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.OSC_put( this ); emu->dispatch.OSC_put( this );
} }
void OSC_Start::act_on_terminal( Terminal::Emulator *emu ) const void OSC_Start::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.OSC_start( this ); emu->dispatch.OSC_start( this );
} }
void OSC_End::act_on_terminal( Terminal::Emulator *emu ) const void OSC_End::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->OSC_end( this ); emu->OSC_end( this );
} }
void UserByte::act_on_terminal( Terminal::Emulator *emu ) const void UserByte::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->fb.ds.application_mode_cursor_keys ) );
emu->fb.ds.application_mode_cursor_keys ) );
} }
void Resize::act_on_terminal( Terminal::Emulator *emu ) const void Resize::act_on_terminal( Terminal::Emulator* emu ) const
{ {
emu->resize( width, height ); emu->resize( width, height );
} }
+115 -105
View File
@@ -38,128 +38,138 @@
#include <vector> #include <vector>
namespace Terminal { namespace Terminal {
class Emulator; class Emulator;
} }
namespace Parser { namespace Parser {
class Action class Action
{ {
public: public:
wchar_t ch; wchar_t ch;
bool char_present; bool char_present;
virtual std::string name( void ) = 0; virtual std::string name( void ) = 0;
virtual void act_on_terminal( Terminal::Emulator * ) const {}; virtual void act_on_terminal( Terminal::Emulator* ) const {};
virtual bool ignore() const { return false; } virtual bool ignore() const { return false; }
Action() : ch( -1 ), char_present( false ) {}; Action() : ch( -1 ), char_present( false ) {};
virtual ~Action() {}; virtual ~Action() {};
}; };
using ActionPointer = std::shared_ptr<Action>; using ActionPointer = std::shared_ptr<Action>;
using Actions = std::vector<ActionPointer>; using Actions = std::vector<ActionPointer>;
class Ignore : public Action { class Ignore : public Action
public: {
std::string name( void ) { return std::string( "Ignore" ); } public:
bool ignore() const { return true; } std::string name( void ) { return std::string( "Ignore" ); }
}; bool ignore() const { return true; }
class Print : public Action { };
public: class Print : public Action
std::string name( void ) { return std::string( "Print" ); } {
void act_on_terminal( Terminal::Emulator *emu ) const; public:
}; std::string name( void ) { return std::string( "Print" ); }
class Execute : public Action { void act_on_terminal( Terminal::Emulator* emu ) const;
public: };
std::string name( void ) { return std::string( "Execute" ); } class Execute : public Action
void act_on_terminal( Terminal::Emulator *emu ) const; {
}; public:
class Clear : public Action { std::string name( void ) { return std::string( "Execute" ); }
public: void act_on_terminal( Terminal::Emulator* emu ) const;
std::string name( void ) { return std::string( "Clear" ); } };
void act_on_terminal( Terminal::Emulator *emu ) const; class Clear : public Action
}; {
class Collect : public Action { public:
public: std::string name( void ) { return std::string( "Clear" ); }
std::string name( void ) { return std::string( "Collect" ); } void act_on_terminal( Terminal::Emulator* emu ) const;
void act_on_terminal( Terminal::Emulator *emu ) const; };
}; class Collect : public Action
class Param : public Action { {
public: public:
std::string name( void ) { return std::string( "Param" ); } std::string name( void ) { return std::string( "Collect" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
}; };
class Esc_Dispatch : public Action { class Param : public Action
public: {
std::string name( void ) { return std::string( "Esc_Dispatch" ); } public:
void act_on_terminal( Terminal::Emulator *emu ) const; std::string name( void ) { return std::string( "Param" ); }
}; void act_on_terminal( Terminal::Emulator* emu ) const;
class CSI_Dispatch : public Action { };
public: class Esc_Dispatch : public Action
std::string name( void ) { return std::string( "CSI_Dispatch" ); } {
void act_on_terminal( Terminal::Emulator *emu ) const; public:
}; std::string name( void ) { return std::string( "Esc_Dispatch" ); }
class Hook : public Action { void act_on_terminal( Terminal::Emulator* emu ) const;
public: std::string name( void ) { return std::string( "Hook" ); } };
}; class CSI_Dispatch : public Action
class Put : public Action { {
public: std::string name( void ) { return std::string( "Put" ); } public:
}; std::string name( void ) { return std::string( "CSI_Dispatch" ); }
class Unhook : public Action { void act_on_terminal( Terminal::Emulator* emu ) const;
public: std::string name( void ) { return std::string( "Unhook" ); } };
}; class Hook : public Action
class OSC_Start : public Action { {
public: public:
std::string name( void ) { return std::string( "OSC_Start" ); } std::string name( void ) { return std::string( "Hook" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; };
}; class Put : public Action
class OSC_Put : public Action { {
public: public:
std::string name( void ) { return std::string( "OSC_Put" ); } std::string name( void ) { return std::string( "Put" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; };
}; class Unhook : public Action
class OSC_End : public Action { {
public: public:
std::string name( void ) { return std::string( "OSC_End" ); } std::string name( void ) { return std::string( "Unhook" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; };
}; class OSC_Start : public Action
{
public:
std::string name( void ) { return std::string( "OSC_Start" ); }
void act_on_terminal( Terminal::Emulator* emu ) const;
};
class OSC_Put : public Action
{
public:
std::string name( void ) { return std::string( "OSC_Put" ); }
void act_on_terminal( Terminal::Emulator* emu ) const;
};
class OSC_End : public Action
{
public:
std::string name( void ) { return std::string( "OSC_End" ); }
void act_on_terminal( Terminal::Emulator* emu ) const;
};
class UserByte : public Action { class UserByte : public Action
/* user keystroke -- not part of the host-source state machine*/ {
public: /* user keystroke -- not part of the host-source state machine*/
char c; /* The user-source byte. We don't try to interpret the charset */ public:
char c; /* The user-source byte. We don't try to interpret the charset */
std::string name( void ) { return std::string( "UserByte" ); } std::string name( void ) { return std::string( "UserByte" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
UserByte( int s_c ) : c( s_c ) {} UserByte( int s_c ) : c( s_c ) {}
bool operator==( const UserByte &other ) const bool operator==( const UserByte& other ) const { return c == other.c; }
{ };
return c == other.c;
}
};
class Resize : public Action { class Resize : public Action
/* resize event -- not part of the host-source state machine*/ {
public: /* resize event -- not part of the host-source state machine*/
size_t width, height; public:
size_t width, height;
std::string name( void ) { return std::string( "Resize" ); } std::string name( void ) { return std::string( "Resize" ); }
void act_on_terminal( Terminal::Emulator *emu ) const; void act_on_terminal( Terminal::Emulator* emu ) const;
Resize( size_t s_width, size_t s_height ) Resize( size_t s_width, size_t s_height ) : width( s_width ), height( s_height ) {}
: width( s_width ),
height( s_height )
{}
bool operator==( const Resize &other ) const bool operator==( const Resize& other ) const { return ( width == other.width ) && ( height == other.height ); }
{ };
return ( width == other.width ) && ( height == other.height );
}
};
} }
#endif #endif
+40 -49
View File
@@ -39,16 +39,14 @@ using namespace Parser;
Transition State::anywhere_rule( wchar_t ch ) const Transition State::anywhere_rule( wchar_t ch ) const
{ {
if ( (ch == 0x18) || (ch == 0x1A) if ( ( ch == 0x18 ) || ( ch == 0x1A ) || ( ( 0x80 <= ch ) && ( ch <= 0x8F ) )
|| ((0x80 <= ch) && (ch <= 0x8F)) || ( ( 0x91 <= ch ) && ( ch <= 0x97 ) ) || ( ch == 0x99 ) || ( ch == 0x9A ) ) {
|| ((0x91 <= ch) && (ch <= 0x97))
|| (ch == 0x99) || (ch == 0x9A) ) {
return Transition( std::make_shared<Execute>(), &family->s_Ground ); return Transition( std::make_shared<Execute>(), &family->s_Ground );
} else if ( ch == 0x9C ) { } else if ( ch == 0x9C ) {
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} else if ( ch == 0x1B ) { } else if ( ch == 0x1B ) {
return Transition( &family->s_Escape ); return Transition( &family->s_Escape );
} else if ( (ch == 0x98) || (ch == 0x9E) || (ch == 0x9F) ) { } else if ( ( ch == 0x98 ) || ( ch == 0x9E ) || ( ch == 0x9F ) ) {
return Transition( &family->s_SOS_PM_APC_String ); return Transition( &family->s_SOS_PM_APC_String );
} else if ( ch == 0x90 ) { } else if ( ch == 0x90 ) {
return Transition( &family->s_DCS_Entry ); return Transition( &family->s_DCS_Entry );
@@ -58,7 +56,7 @@ Transition State::anywhere_rule( wchar_t ch ) const
return Transition( &family->s_CSI_Entry ); return Transition( &family->s_CSI_Entry );
} }
return Transition(( State * )NULL, ActionPointer() ); /* don't allocate an Ignore action */ return Transition( (State*)NULL, ActionPointer() ); /* don't allocate an Ignore action */
} }
Transition State::input( wchar_t ch ) const Transition State::input( wchar_t ch ) const
@@ -80,15 +78,13 @@ Transition State::input( wchar_t ch ) const
static bool C0_prime( wchar_t ch ) static bool C0_prime( wchar_t ch )
{ {
return (ch <= 0x17) return ( ch <= 0x17 ) || ( ch == 0x19 ) || ( ( 0x1C <= ch ) && ( ch <= 0x1F ) );
|| (ch == 0x19)
|| ( (0x1C <= ch) && (ch <= 0x1F) );
} }
static bool GLGR ( wchar_t ch ) static bool GLGR( wchar_t ch )
{ {
return ( (0x20 <= ch) && (ch <= 0x7F) ) /* GL area */ return ( ( 0x20 <= ch ) && ( ch <= 0x7F ) ) /* GL area */
|| ( (0xA0 <= ch) && (ch <= 0xFF) ); /* GR area */ || ( ( 0xA0 <= ch ) && ( ch <= 0xFF ) ); /* GR area */
} }
Transition Ground::input_state_rule( wchar_t ch ) const Transition Ground::input_state_rule( wchar_t ch ) const
@@ -115,16 +111,12 @@ Transition Escape::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_Escape_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_Escape_Intermediate );
} }
if ( ( (0x30 <= ch) && (ch <= 0x4F) ) if ( ( ( 0x30 <= ch ) && ( ch <= 0x4F ) ) || ( ( 0x51 <= ch ) && ( ch <= 0x57 ) ) || ( ch == 0x59 )
|| ( (0x51 <= ch) && (ch <= 0x57) ) || ( ch == 0x5A ) || ( ch == 0x5C ) || ( ( 0x60 <= ch ) && ( ch <= 0x7E ) ) ) {
|| ( ch == 0x59 )
|| ( ch == 0x5A )
|| ( ch == 0x5C )
|| ( (0x60 <= ch) && (ch <= 0x7E) ) ) {
return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground );
} }
@@ -140,7 +132,7 @@ Transition Escape::input_state_rule( wchar_t ch ) const
return Transition( &family->s_DCS_Entry ); return Transition( &family->s_DCS_Entry );
} }
if ( (ch == 0x58) || (ch == 0x5E) || (ch == 0x5F) ) { if ( ( ch == 0x58 ) || ( ch == 0x5E ) || ( ch == 0x5F ) ) {
return Transition( &family->s_SOS_PM_APC_String ); return Transition( &family->s_SOS_PM_APC_String );
} }
@@ -153,11 +145,11 @@ Transition Escape_Intermediate::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x30 <= ch) && (ch <= 0x7E) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<Esc_Dispatch>(), &family->s_Ground );
} }
@@ -175,16 +167,15 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
|| ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>(), &family->s_CSI_Param ); return Transition( std::make_shared<Param>(), &family->s_CSI_Param );
} }
if ( (0x3C <= ch) && (ch <= 0x3F) ) { if ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Param ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Param );
} }
@@ -192,7 +183,7 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate );
} }
@@ -205,19 +196,19 @@ Transition CSI_Param::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>() ); return Transition( std::make_shared<Param>() );
} }
if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { if ( ( ch == 0x3A ) || ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) ) {
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_CSI_Intermediate );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
@@ -230,15 +221,15 @@ Transition CSI_Intermediate::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground ); return Transition( std::make_shared<CSI_Dispatch>(), &family->s_Ground );
} }
if ( (0x30 <= ch) && (ch <= 0x3F) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x3F ) ) {
return Transition( &family->s_CSI_Ignore ); return Transition( &family->s_CSI_Ignore );
} }
@@ -251,7 +242,7 @@ Transition CSI_Ignore::input_state_rule( wchar_t ch ) const
return Transition( std::make_shared<Execute>() ); return Transition( std::make_shared<Execute>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} }
@@ -265,7 +256,7 @@ ActionPointer DCS_Entry::enter( void ) const
Transition DCS_Entry::input_state_rule( wchar_t ch ) const Transition DCS_Entry::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate );
} }
@@ -273,15 +264,15 @@ Transition DCS_Entry::input_state_rule( wchar_t ch ) const
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>(), &family->s_DCS_Param ); return Transition( std::make_shared<Param>(), &family->s_DCS_Param );
} }
if ( (0x3C <= ch) && (ch <= 0x3F) ) { if ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Param ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Param );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
@@ -290,19 +281,19 @@ Transition DCS_Entry::input_state_rule( wchar_t ch ) const
Transition DCS_Param::input_state_rule( wchar_t ch ) const Transition DCS_Param::input_state_rule( wchar_t ch ) const
{ {
if ( ( (0x30 <= ch) && (ch <= 0x39) ) || ( ch == 0x3B ) ) { if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) {
return Transition( std::make_shared<Param>() ); return Transition( std::make_shared<Param>() );
} }
if ( ( ch == 0x3A ) || ( (0x3C <= ch) && (ch <= 0x3F) ) ) { if ( ( ch == 0x3A ) || ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) ) {
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate ); return Transition( std::make_shared<Collect>(), &family->s_DCS_Intermediate );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
@@ -311,15 +302,15 @@ Transition DCS_Param::input_state_rule( wchar_t ch ) const
Transition DCS_Intermediate::input_state_rule( wchar_t ch ) const Transition DCS_Intermediate::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x2F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) {
return Transition( std::make_shared<Collect>() ); return Transition( std::make_shared<Collect>() );
} }
if ( (0x40 <= ch) && (ch <= 0x7E) ) { if ( ( 0x40 <= ch ) && ( ch <= 0x7E ) ) {
return Transition( &family->s_DCS_Passthrough ); return Transition( &family->s_DCS_Passthrough );
} }
if ( (0x30 <= ch) && (ch <= 0x3F) ) { if ( ( 0x30 <= ch ) && ( ch <= 0x3F ) ) {
return Transition( &family->s_DCS_Ignore ); return Transition( &family->s_DCS_Ignore );
} }
@@ -338,7 +329,7 @@ ActionPointer DCS_Passthrough::exit( void ) const
Transition DCS_Passthrough::input_state_rule( wchar_t ch ) const Transition DCS_Passthrough::input_state_rule( wchar_t ch ) const
{ {
if ( C0_prime( ch ) || ( (0x20 <= ch) && (ch <= 0x7E) ) ) { if ( C0_prime( ch ) || ( ( 0x20 <= ch ) && ( ch <= 0x7E ) ) ) {
return Transition( std::make_shared<Put>() ); return Transition( std::make_shared<Put>() );
} }
@@ -370,11 +361,11 @@ ActionPointer OSC_String::exit( void ) const
Transition OSC_String::input_state_rule( wchar_t ch ) const Transition OSC_String::input_state_rule( wchar_t ch ) const
{ {
if ( (0x20 <= ch) && (ch <= 0x7F) ) { if ( ( 0x20 <= ch ) && ( ch <= 0x7F ) ) {
return Transition( std::make_shared<OSC_Put>() ); return Transition( std::make_shared<OSC_Put>() );
} }
if ( (ch == 0x9C) || (ch == 0x07) ) { /* 0x07 is xterm non-ANSI variant */ if ( ( ch == 0x9C ) || ( ch == 0x07 ) ) { /* 0x07 is xterm non-ANSI variant */
return Transition( &family->s_Ground ); return Transition( &family->s_Ground );
} }
+81 -67
View File
@@ -36,84 +36,98 @@
#include "parsertransition.h" #include "parsertransition.h"
namespace Parser { namespace Parser {
class StateFamily; class StateFamily;
class State class State
{ {
protected: protected:
virtual Transition input_state_rule( wchar_t ch ) const = 0; virtual Transition input_state_rule( wchar_t ch ) const = 0;
StateFamily *family; StateFamily* family;
private: private:
Transition anywhere_rule( wchar_t ch ) const; Transition anywhere_rule( wchar_t ch ) const;
public: public:
void setfamily( StateFamily *s_family ) { family = s_family; } void setfamily( StateFamily* s_family ) { family = s_family; }
Transition input( wchar_t ch ) const; Transition input( wchar_t ch ) const;
virtual ActionPointer enter( void ) const { return std::make_shared<Ignore>(); } virtual ActionPointer enter( void ) const { return std::make_shared<Ignore>(); }
virtual ActionPointer exit( void ) const { return std::make_shared<Ignore>(); } virtual ActionPointer exit( void ) const { return std::make_shared<Ignore>(); }
State() : family( NULL ) {}; State() : family( NULL ) {};
virtual ~State() {}; virtual ~State() {};
State( const State & ); State( const State& );
State & operator=( const State & ); State& operator=( const State& );
}; };
class Ground : public State { class Ground : public State
Transition input_state_rule( wchar_t ch ) const; {
}; Transition input_state_rule( wchar_t ch ) const;
};
class Escape : public State { class Escape : public State
ActionPointer enter( void ) const; {
Transition input_state_rule( wchar_t ch ) const; ActionPointer enter( void ) const;
}; Transition input_state_rule( wchar_t ch ) const;
};
class Escape_Intermediate : public State { class Escape_Intermediate : public State
Transition input_state_rule( wchar_t ch ) const; {
}; Transition input_state_rule( wchar_t ch ) const;
};
class CSI_Entry : public State { class CSI_Entry : public State
ActionPointer enter( void ) const; {
Transition input_state_rule( wchar_t ch ) const; ActionPointer enter( void ) const;
}; Transition input_state_rule( wchar_t ch ) const;
class CSI_Param : public State { };
Transition input_state_rule( wchar_t ch ) const; class CSI_Param : public State
}; {
class CSI_Intermediate : public State { Transition input_state_rule( wchar_t ch ) const;
Transition input_state_rule( wchar_t ch ) const; };
}; class CSI_Intermediate : public State
class CSI_Ignore : public State { {
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; };
class CSI_Ignore : public State
{
Transition input_state_rule( wchar_t ch ) const;
};
class DCS_Entry : public State { class DCS_Entry : public State
ActionPointer enter( void ) const; {
Transition input_state_rule( wchar_t ch ) const; ActionPointer enter( void ) const;
}; Transition input_state_rule( wchar_t ch ) const;
class DCS_Param : public State { };
Transition input_state_rule( wchar_t ch ) const; class DCS_Param : public State
}; {
class DCS_Intermediate : public State { Transition input_state_rule( wchar_t ch ) const;
Transition input_state_rule( wchar_t ch ) const; };
}; class DCS_Intermediate : public State
class DCS_Passthrough : public State { {
ActionPointer enter( void ) const; Transition input_state_rule( wchar_t ch ) const;
Transition input_state_rule( wchar_t ch ) const; };
ActionPointer exit( void ) const; class DCS_Passthrough : public State
}; {
class DCS_Ignore : public State { ActionPointer enter( void ) const;
Transition input_state_rule( wchar_t ch ) const; Transition input_state_rule( wchar_t ch ) const;
}; ActionPointer exit( void ) const;
};
class DCS_Ignore : public State
{
Transition input_state_rule( wchar_t ch ) const;
};
class OSC_String : public State { class OSC_String : public State
ActionPointer enter( void ) const; {
Transition input_state_rule( wchar_t ch ) const; ActionPointer enter( void ) const;
ActionPointer exit( void ) const; Transition input_state_rule( wchar_t ch ) const;
}; ActionPointer exit( void ) const;
class SOS_PM_APC_String : public State { };
Transition input_state_rule( wchar_t ch ) const; class SOS_PM_APC_String : public State
}; {
Transition input_state_rule( wchar_t ch ) const;
};
} }
#endif #endif
+42 -44
View File
@@ -36,51 +36,49 @@
#include "parserstate.h" #include "parserstate.h"
namespace Parser { namespace Parser {
class StateFamily class StateFamily
{
public:
Ground s_Ground;
Escape s_Escape;
Escape_Intermediate s_Escape_Intermediate;
CSI_Entry s_CSI_Entry;
CSI_Param s_CSI_Param;
CSI_Intermediate s_CSI_Intermediate;
CSI_Ignore s_CSI_Ignore;
DCS_Entry s_DCS_Entry;
DCS_Param s_DCS_Param;
DCS_Intermediate s_DCS_Intermediate;
DCS_Passthrough s_DCS_Passthrough;
DCS_Ignore s_DCS_Ignore;
OSC_String s_OSC_String;
SOS_PM_APC_String s_SOS_PM_APC_String;
StateFamily()
: s_Ground(), s_Escape(), s_Escape_Intermediate(), s_CSI_Entry(), s_CSI_Param(), s_CSI_Intermediate(),
s_CSI_Ignore(), s_DCS_Entry(), s_DCS_Param(), s_DCS_Intermediate(), s_DCS_Passthrough(), s_DCS_Ignore(),
s_OSC_String(), s_SOS_PM_APC_String()
{ {
public: s_Ground.setfamily( this );
Ground s_Ground; s_Escape.setfamily( this );
s_Escape_Intermediate.setfamily( this );
Escape s_Escape; s_CSI_Entry.setfamily( this );
Escape_Intermediate s_Escape_Intermediate; s_CSI_Param.setfamily( this );
s_CSI_Intermediate.setfamily( this );
CSI_Entry s_CSI_Entry; s_CSI_Ignore.setfamily( this );
CSI_Param s_CSI_Param; s_DCS_Entry.setfamily( this );
CSI_Intermediate s_CSI_Intermediate; s_DCS_Param.setfamily( this );
CSI_Ignore s_CSI_Ignore; s_DCS_Intermediate.setfamily( this );
s_DCS_Passthrough.setfamily( this );
DCS_Entry s_DCS_Entry; s_DCS_Ignore.setfamily( this );
DCS_Param s_DCS_Param; s_OSC_String.setfamily( this );
DCS_Intermediate s_DCS_Intermediate; s_SOS_PM_APC_String.setfamily( this );
DCS_Passthrough s_DCS_Passthrough; }
DCS_Ignore s_DCS_Ignore; };
OSC_String s_OSC_String;
SOS_PM_APC_String s_SOS_PM_APC_String;
StateFamily()
: s_Ground(), s_Escape(), s_Escape_Intermediate(),
s_CSI_Entry(), s_CSI_Param(), s_CSI_Intermediate(), s_CSI_Ignore(),
s_DCS_Entry(), s_DCS_Param(), s_DCS_Intermediate(),
s_DCS_Passthrough(), s_DCS_Ignore(),
s_OSC_String(), s_SOS_PM_APC_String()
{
s_Ground.setfamily( this );
s_Escape.setfamily( this );
s_Escape_Intermediate.setfamily( this );
s_CSI_Entry.setfamily( this );
s_CSI_Param.setfamily( this );
s_CSI_Intermediate.setfamily( this );
s_CSI_Ignore.setfamily( this );
s_DCS_Entry.setfamily( this );
s_DCS_Param.setfamily( this );
s_DCS_Intermediate.setfamily( this );
s_DCS_Passthrough.setfamily( this );
s_DCS_Ignore.setfamily( this );
s_OSC_String.setfamily( this );
s_SOS_PM_APC_String.setfamily( this );
}
};
} }
#endif #endif
+25 -27
View File
@@ -38,37 +38,35 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
namespace Parser { namespace Parser {
class State; class State;
class Transition class Transition
{
public:
// Transition is only a courier for an Action; it should
// never create/delete one on its own.
ActionPointer action;
State* next_state;
Transition( const Transition& x ) : action( x.action ), next_state( x.next_state ) {}
Transition& operator=( const Transition& t )
{ {
public: action = t.action;
// Transition is only a courier for an Action; it should next_state = t.next_state;
// never create/delete one on its own.
ActionPointer action;
State *next_state;
Transition( const Transition &x ) return *this;
: action( x.action ), }
next_state( x.next_state ) {} Transition( ActionPointer s_action = std::make_shared<Ignore>(), State* s_next_state = NULL )
Transition & operator=( const Transition &t ) : action( s_action ), next_state( s_next_state )
{ {}
action = t.action;
next_state = t.next_state;
return *this; // This is only ever used in the 1-argument form;
} // we use this instead of an initializer to
Transition( ActionPointer s_action = std::make_shared<Ignore>(), State *s_next_state=NULL ) // tell Coverity the object never owns *action.
: action( s_action ), next_state( s_next_state ) Transition( State* s_next_state, ActionPointer s_action = std::make_shared<Ignore>() )
{} : action( s_action ), next_state( s_next_state )
{}
// This is only ever used in the 1-argument form; };
// we use this instead of an initializer to
// tell Coverity the object never owns *action.
Transition( State *s_next_state, ActionPointer s_action = std::make_shared<Ignore>() )
: action( s_action ), next_state( s_next_state )
{}
};
} }
#endif #endif
+67 -74
View File
@@ -41,9 +41,7 @@
using namespace Terminal; using namespace Terminal;
Emulator::Emulator( size_t s_width, size_t s_height ) Emulator::Emulator( size_t s_width, size_t s_height ) : fb( s_width, s_height ), dispatch(), user() {}
: fb( s_width, s_height ), dispatch(), user()
{}
std::string Emulator::read_octets_to_host( void ) std::string Emulator::read_octets_to_host( void )
{ {
@@ -52,12 +50,12 @@ std::string Emulator::read_octets_to_host( void )
return ret; return ret;
} }
void Emulator::execute( const Parser::Execute *act ) void Emulator::execute( const Parser::Execute* act )
{ {
dispatch.dispatch( CONTROL, act, &fb ); dispatch.dispatch( CONTROL, act, &fb );
} }
void Emulator::print( const Parser::Print *act ) void Emulator::print( const Parser::Print* act )
{ {
assert( act->char_present ); assert( act->char_present );
@@ -67,103 +65,98 @@ void Emulator::print( const Parser::Print *act )
* Check for printing ISO 8859-1 first, it's a cheap way to detect * Check for printing ISO 8859-1 first, it's a cheap way to detect
* some common narrow characters. * some common narrow characters.
*/ */
const int chwidth = ch == L'\0' ? -1 : ( Cell::isprint_iso8859_1( ch ) ? 1 : wcwidth( ch )); const int chwidth = ch == L'\0' ? -1 : ( Cell::isprint_iso8859_1( ch ) ? 1 : wcwidth( ch ) );
Cell *this_cell = fb.get_mutable_cell(); Cell* this_cell = fb.get_mutable_cell();
switch ( chwidth ) { switch ( chwidth ) {
case 1: /* normal character */ case 1: /* normal character */
case 2: /* wide character */ case 2: /* wide character */
if ( fb.ds.auto_wrap_mode && fb.ds.next_print_will_wrap ) { if ( fb.ds.auto_wrap_mode && fb.ds.next_print_will_wrap ) {
fb.get_mutable_row( -1 )->set_wrap( true ); fb.get_mutable_row( -1 )->set_wrap( true );
fb.ds.move_col( 0 ); fb.ds.move_col( 0 );
fb.move_rows_autoscroll( 1 ); fb.move_rows_autoscroll( 1 );
this_cell = NULL; this_cell = NULL;
} else if ( fb.ds.auto_wrap_mode } else if ( fb.ds.auto_wrap_mode && ( chwidth == 2 )
&& (chwidth == 2) && ( fb.ds.get_cursor_col() == fb.ds.get_width() - 1 ) ) {
&& (fb.ds.get_cursor_col() == fb.ds.get_width() - 1) ) { /* wrap 2-cell chars if no room, even without will-wrap flag */
/* wrap 2-cell chars if no room, even without will-wrap flag */ fb.reset_cell( this_cell );
fb.reset_cell( this_cell ); fb.get_mutable_row( -1 )->set_wrap( false );
fb.get_mutable_row( -1 )->set_wrap( false ); /* There doesn't seem to be a consistent way to get the
/* There doesn't seem to be a consistent way to get the downstream terminal emulator to set the wrap-around
downstream terminal emulator to set the wrap-around copy-and-paste flag on a row that ends with an empty cell
copy-and-paste flag on a row that ends with an empty cell because a wide char was wrapped to the next line. */
because a wide char was wrapped to the next line. */ fb.ds.move_col( 0 );
fb.ds.move_col( 0 ); fb.move_rows_autoscroll( 1 );
fb.move_rows_autoscroll( 1 ); this_cell = NULL;
this_cell = NULL;
}
if ( fb.ds.insert_mode ) {
for ( int i = 0; i < chwidth; i++ ) {
fb.insert_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() );
} }
this_cell = NULL;
}
if (!this_cell) { if ( fb.ds.insert_mode ) {
this_cell = fb.get_mutable_cell(); for ( int i = 0; i < chwidth; i++ ) {
} fb.insert_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() );
}
this_cell = NULL;
}
fb.reset_cell( this_cell ); if ( !this_cell ) {
this_cell->append( ch ); this_cell = fb.get_mutable_cell();
this_cell->set_wide( chwidth == 2 ); /* chwidth had better be 1 or 2 here */ }
fb.apply_renditions_to_cell( this_cell );
if ( chwidth == 2 fb.reset_cell( this_cell );
&& fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { /* erase overlapped cell */ this_cell->append( ch );
fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); this_cell->set_wide( chwidth == 2 ); /* chwidth had better be 1 or 2 here */
} fb.apply_renditions_to_cell( this_cell );
fb.ds.move_col( chwidth, true, true ); if ( chwidth == 2 && fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { /* erase overlapped cell */
fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) );
}
break; fb.ds.move_col( chwidth, true, true );
case 0: /* combining character */
break;
case 0: /* combining character */
{ {
Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ Cell* combining_cell = fb.get_combining_cell(); /* can be null if we were resized */
if ( combining_cell == NULL ) { /* character is now offscreen */ if ( combining_cell == NULL ) { /* character is now offscreen */
break; break;
} }
if ( combining_cell->empty() ) { if ( combining_cell->empty() ) {
/* cell starts with combining character */ /* cell starts with combining character */
/* ... but isn't necessarily the target for a new /* ... but isn't necessarily the target for a new
base character [e.g. start of line], if the base character [e.g. start of line], if the
combining character has been cleared with combining character has been cleared with
a sequence like ED ("J") or EL ("K") */ a sequence like ED ("J") or EL ("K") */
assert( !combining_cell->get_wide() ); assert( !combining_cell->get_wide() );
combining_cell->set_fallback( true ); combining_cell->set_fallback( true );
fb.ds.move_col( 1, true, true ); fb.ds.move_col( 1, true, true );
} }
if ( !combining_cell->full() ) { if ( !combining_cell->full() ) {
combining_cell->append( ch ); combining_cell->append( ch );
} }
} } break;
break; case -1: /* unprintable character */
case -1: /* unprintable character */ break;
break; default:
default: assert( !"unexpected character width from wcwidth()" );
assert( !"unexpected character width from wcwidth()" ); break;
break;
} }
} }
void Emulator::CSI_dispatch( const Parser::CSI_Dispatch *act ) void Emulator::CSI_dispatch( const Parser::CSI_Dispatch* act )
{ {
dispatch.dispatch( CSI, act, &fb ); dispatch.dispatch( CSI, act, &fb );
} }
void Emulator::OSC_end( const Parser::OSC_End *act ) void Emulator::OSC_end( const Parser::OSC_End* act )
{ {
dispatch.OSC_dispatch( act, &fb ); dispatch.OSC_dispatch( act, &fb );
} }
void Emulator::Esc_dispatch( const Parser::Esc_Dispatch *act ) void Emulator::Esc_dispatch( const Parser::Esc_Dispatch* act )
{ {
/* handle 7-bit ESC-encoding of C1 control characters */ /* handle 7-bit ESC-encoding of C1 control characters */
if ( (dispatch.get_dispatch_chars().size() == 0) if ( ( dispatch.get_dispatch_chars().size() == 0 ) && ( 0x40 <= act->ch ) && ( act->ch <= 0x5F ) ) {
&& (0x40 <= act->ch)
&& (act->ch <= 0x5F) ) {
Parser::Esc_Dispatch act2 = *act; Parser::Esc_Dispatch act2 = *act;
act2.ch += 0x40; act2.ch += 0x40;
dispatch.dispatch( CONTROL, &act2, &fb ); dispatch.dispatch( CONTROL, &act2, &fb );
@@ -177,7 +170,7 @@ void Emulator::resize( size_t s_width, size_t s_height )
fb.resize( s_width, s_height ); fb.resize( s_width, s_height );
} }
bool Emulator::operator==( Emulator const &x ) const bool Emulator::operator==( Emulator const& x ) const
{ {
/* dispatcher and user are irrelevant for us */ /* dispatcher and user are irrelevant for us */
return fb == x.fb; return fb == x.fb;
+32 -31
View File
@@ -41,47 +41,48 @@
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h" #include "terminaldispatcher.h"
#include "terminaluserinput.h"
#include "terminaldisplay.h" #include "terminaldisplay.h"
#include "terminaluserinput.h"
namespace Terminal { namespace Terminal {
class Emulator { class Emulator
friend void Parser::Print::act_on_terminal( Emulator * ) const; {
friend void Parser::Execute::act_on_terminal( Emulator * ) const; friend void Parser::Print::act_on_terminal( Emulator* ) const;
friend void Parser::Clear::act_on_terminal( Emulator * ) const; friend void Parser::Execute::act_on_terminal( Emulator* ) const;
friend void Parser::Param::act_on_terminal( Emulator * ) const; friend void Parser::Clear::act_on_terminal( Emulator* ) const;
friend void Parser::Collect::act_on_terminal( Emulator * ) const; friend void Parser::Param::act_on_terminal( Emulator* ) const;
friend void Parser::CSI_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::Collect::act_on_terminal( Emulator* ) const;
friend void Parser::Esc_Dispatch::act_on_terminal( Emulator * ) const; friend void Parser::CSI_Dispatch::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_Start::act_on_terminal( Emulator * ) const; friend void Parser::Esc_Dispatch::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_Put::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Start::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_End::act_on_terminal( Emulator * ) const; friend void Parser::OSC_Put::act_on_terminal( Emulator* ) const;
friend void Parser::OSC_End::act_on_terminal( Emulator* ) const;
friend void Parser::UserByte::act_on_terminal( Emulator * ) const; friend void Parser::UserByte::act_on_terminal( Emulator* ) const;
friend void Parser::Resize::act_on_terminal( Emulator * ) const; friend void Parser::Resize::act_on_terminal( Emulator* ) const;
private: private:
Framebuffer fb; Framebuffer fb;
Dispatcher dispatch; Dispatcher dispatch;
UserInput user; UserInput user;
/* action methods */ /* action methods */
void print( const Parser::Print *act ); void print( const Parser::Print* act );
void execute( const Parser::Execute *act ); void execute( const Parser::Execute* act );
void CSI_dispatch( const Parser::CSI_Dispatch *act ); void CSI_dispatch( const Parser::CSI_Dispatch* act );
void Esc_dispatch( const Parser::Esc_Dispatch *act ); void Esc_dispatch( const Parser::Esc_Dispatch* act );
void OSC_end( const Parser::OSC_End *act ); void OSC_end( const Parser::OSC_End* act );
void resize( size_t s_width, size_t s_height ); void resize( size_t s_width, size_t s_height );
public: public:
Emulator( size_t s_width, size_t s_height ); Emulator( size_t s_width, size_t s_height );
std::string read_octets_to_host( void ); std::string read_octets_to_host( void );
const Framebuffer & get_fb( void ) const { return fb; } const Framebuffer& get_fb( void ) const { return fb; }
bool operator==( Emulator const &x ) const; bool operator==( Emulator const& x ) const;
}; };
} }
#endif #endif
+53 -52
View File
@@ -36,23 +36,22 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include "terminaldispatcher.h"
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h"
using namespace Terminal; using namespace Terminal;
static const size_t MAXIMUM_CLIPBOARD_SIZE = 16*1024; static const size_t MAXIMUM_CLIPBOARD_SIZE = 16 * 1024;
Dispatcher::Dispatcher() Dispatcher::Dispatcher()
: params(), parsed_params(), parsed( false ), dispatch_chars(), : params(), parsed_params(), parsed( false ), dispatch_chars(), OSC_string(), terminal_to_host()
OSC_string(), terminal_to_host()
{} {}
void Dispatcher::newparamchar( const Parser::Param *act ) void Dispatcher::newparamchar( const Parser::Param* act )
{ {
assert( act->char_present ); assert( act->char_present );
assert( (act->ch == ';') || ( (act->ch >= '0') && (act->ch <= '9') ) ); assert( ( act->ch == ';' ) || ( ( act->ch >= '0' ) && ( act->ch <= '9' ) ) );
if ( params.length() < 100 ) { if ( params.length() < 100 ) {
/* enough for 16 five-char params plus 15 semicolons */ /* enough for 16 five-char params plus 15 semicolons */
params.push_back( act->ch ); params.push_back( act->ch );
@@ -60,16 +59,16 @@ void Dispatcher::newparamchar( const Parser::Param *act )
parsed = false; parsed = false;
} }
void Dispatcher::collect( const Parser::Collect *act ) void Dispatcher::collect( const Parser::Collect* act )
{ {
assert( act->char_present ); assert( act->char_present );
if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */ if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */
&& ( act->ch <= 255 ) ) { /* ignore non-8-bit */ && ( act->ch <= 255 ) ) { /* ignore non-8-bit */
dispatch_chars.push_back( act->ch ); dispatch_chars.push_back( act->ch );
} }
} }
void Dispatcher::clear( const Parser::Clear *act __attribute((unused)) ) void Dispatcher::clear( const Parser::Clear* act __attribute( ( unused ) ) )
{ {
params.clear(); params.clear();
dispatch_chars.clear(); dispatch_chars.clear();
@@ -83,17 +82,17 @@ void Dispatcher::parse_params( void )
} }
parsed_params.clear(); parsed_params.clear();
const char *str = params.c_str(); const char* str = params.c_str();
const char *segment_begin = str; const char* segment_begin = str;
while ( 1 ) { while ( 1 ) {
const char *segment_end = strchr( segment_begin, ';' ); const char* segment_end = strchr( segment_begin, ';' );
if ( segment_end == NULL ) { if ( segment_end == NULL ) {
break; break;
} }
errno = 0; errno = 0;
char *endptr; char* endptr;
long val = strtol( segment_begin, &endptr, 10 ); long val = strtol( segment_begin, &endptr, 10 );
if ( endptr == segment_begin ) { if ( endptr == segment_begin ) {
val = -1; val = -1;
@@ -113,7 +112,7 @@ void Dispatcher::parse_params( void )
/* get last param */ /* get last param */
errno = 0; errno = 0;
char *endptr; char* endptr;
long val = strtol( segment_begin, &endptr, 10 ); long val = strtol( segment_begin, &endptr, 10 );
if ( endptr == segment_begin ) { if ( endptr == segment_begin ) {
val = -1; val = -1;
@@ -139,10 +138,11 @@ int Dispatcher::getparam( size_t N, int defaultval )
} }
if ( parsed_params.size() > N ) { if ( parsed_params.size() > N ) {
ret = parsed_params[ N ]; ret = parsed_params[N];
} }
if ( ret < 1 ) ret = defaultval; if ( ret < 1 )
ret = defaultval;
return ret; return ret;
} }
@@ -158,48 +158,46 @@ int Dispatcher::param_count( void )
std::string Dispatcher::str( void ) std::string Dispatcher::str( void )
{ {
char assum[ 64 ]; char assum[64];
snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", snprintf( assum, 64, "[dispatch=\"%s\" params=\"%s\"]", dispatch_chars.c_str(), params.c_str() );
dispatch_chars.c_str(), params.c_str() );
return std::string( assum ); return std::string( assum );
} }
/* construct on first use to avoid static initialization order crash */ /* construct on first use to avoid static initialization order crash */
DispatchRegistry & Terminal::get_global_dispatch_registry( void ) DispatchRegistry& Terminal::get_global_dispatch_registry( void )
{ {
static DispatchRegistry global_dispatch_registry; static DispatchRegistry global_dispatch_registry;
return global_dispatch_registry; return global_dispatch_registry;
} }
static void register_function( Function_Type type, static void register_function( Function_Type type, const std::string& dispatch_chars, Function f )
const std::string & dispatch_chars,
Function f )
{ {
switch ( type ) { switch ( type ) {
case ESCAPE: case ESCAPE:
get_global_dispatch_registry().escape.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); get_global_dispatch_registry().escape.insert( dispatch_map_t::value_type( dispatch_chars, f ) );
break; break;
case CSI: case CSI:
get_global_dispatch_registry().CSI.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); get_global_dispatch_registry().CSI.insert( dispatch_map_t::value_type( dispatch_chars, f ) );
break; break;
case CONTROL: case CONTROL:
get_global_dispatch_registry().control.insert( dispatch_map_t::value_type( dispatch_chars, f ) ); get_global_dispatch_registry().control.insert( dispatch_map_t::value_type( dispatch_chars, f ) );
break; break;
} }
} }
Function::Function( Function_Type type, const std::string & dispatch_chars, Function::Function( Function_Type type,
void (*s_function)( Framebuffer *, Dispatcher * ), const std::string& dispatch_chars,
bool s_clears_wrap_state ) void ( *s_function )( Framebuffer*, Dispatcher* ),
bool s_clears_wrap_state )
: function( s_function ), clears_wrap_state( s_clears_wrap_state ) : function( s_function ), clears_wrap_state( s_clears_wrap_state )
{ {
register_function( type, dispatch_chars, *this ); register_function( type, dispatch_chars, *this );
} }
void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ) void Dispatcher::dispatch( Function_Type type, const Parser::Action* act, Framebuffer* fb )
{ {
/* add final char to dispatch key */ /* add final char to dispatch key */
if ( (type == ESCAPE) || (type == CSI) ) { if ( ( type == ESCAPE ) || ( type == CSI ) ) {
assert( act->char_present ); assert( act->char_present );
Parser::Collect act2; Parser::Collect act2;
act2.char_present = true; act2.char_present = true;
@@ -207,17 +205,23 @@ void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Frameb
collect( &act2 ); collect( &act2 );
} }
dispatch_map_t *map = NULL; dispatch_map_t* map = NULL;
switch ( type ) { switch ( type ) {
case ESCAPE: map = &get_global_dispatch_registry().escape; break; case ESCAPE:
case CSI: map = &get_global_dispatch_registry().CSI; break; map = &get_global_dispatch_registry().escape;
case CONTROL: map = &get_global_dispatch_registry().control; break; break;
case CSI:
map = &get_global_dispatch_registry().CSI;
break;
case CONTROL:
map = &get_global_dispatch_registry().control;
break;
} }
std::string key = dispatch_chars; std::string key = dispatch_chars;
if ( type == CONTROL ) { if ( type == CONTROL ) {
assert( act->ch <= 255 ); assert( act->ch <= 255 );
char ctrlstr[ 2 ] = { (char)act->ch, 0 }; char ctrlstr[2] = { (char)act->ch, 0 };
key = std::string( ctrlstr, 1 ); key = std::string( ctrlstr, 1 );
} }
@@ -233,25 +237,22 @@ void Dispatcher::dispatch( Function_Type type, const Parser::Action *act, Frameb
i->second.function( fb, this ); i->second.function( fb, this );
} }
void Dispatcher::OSC_put( const Parser::OSC_Put *act ) void Dispatcher::OSC_put( const Parser::OSC_Put* act )
{ {
assert( act->char_present ); assert( act->char_present );
if ( OSC_string.size() < MAXIMUM_CLIPBOARD_SIZE) { if ( OSC_string.size() < MAXIMUM_CLIPBOARD_SIZE ) {
OSC_string.push_back( act->ch ); OSC_string.push_back( act->ch );
} }
} }
void Dispatcher::OSC_start( const Parser::OSC_Start *act __attribute((unused)) ) void Dispatcher::OSC_start( const Parser::OSC_Start* act __attribute( ( unused ) ) )
{ {
OSC_string.clear(); OSC_string.clear();
} }
bool Dispatcher::operator==( const Dispatcher &x ) const bool Dispatcher::operator==( const Dispatcher& x ) const
{ {
return ( params == x.params ) return ( params == x.params ) && ( parsed_params == x.parsed_params ) && ( parsed == x.parsed )
&& ( parsed_params == x.parsed_params ) && ( dispatch_chars == x.dispatch_chars ) && ( OSC_string == x.OSC_string )
&& ( parsed == x.parsed ) && ( terminal_to_host == x.terminal_to_host );
&& ( dispatch_chars == x.dispatch_chars )
&& ( OSC_string == x.OSC_string )
&& ( terminal_to_host == x.terminal_to_host );
} }
+69 -60
View File
@@ -33,89 +33,98 @@
#ifndef TERMINALDISPATCHER_HPP #ifndef TERMINALDISPATCHER_HPP
#define TERMINALDISPATCHER_HPP #define TERMINALDISPATCHER_HPP
#include <vector>
#include <string>
#include <map> #include <map>
#include <string>
#include <vector>
namespace Parser { namespace Parser {
class Action; class Action;
class Param; class Param;
class Collect; class Collect;
class Clear; class Clear;
class Esc_Dispatch; class Esc_Dispatch;
class CSI_Dispatch; class CSI_Dispatch;
class Execute; class Execute;
class OSC_Start; class OSC_Start;
class OSC_Put; class OSC_Put;
class OSC_End; class OSC_End;
} }
namespace Terminal { namespace Terminal {
class Framebuffer; class Framebuffer;
class Dispatcher; class Dispatcher;
enum Function_Type { ESCAPE, CSI, CONTROL }; enum Function_Type
{
ESCAPE,
CSI,
CONTROL
};
class Function { class Function
public: {
Function() : function( NULL ), clears_wrap_state( true ) {} public:
Function( Function_Type type, const std::string & dispatch_chars, Function() : function( NULL ), clears_wrap_state( true ) {}
void (*s_function)( Framebuffer *, Dispatcher * ), Function( Function_Type type,
bool s_clears_wrap_state = true ); const std::string& dispatch_chars,
void (*function)( Framebuffer *, Dispatcher * ); void ( *s_function )( Framebuffer*, Dispatcher* ),
bool clears_wrap_state; bool s_clears_wrap_state = true );
}; void ( *function )( Framebuffer*, Dispatcher* );
bool clears_wrap_state;
};
using dispatch_map_t = std::map<std::string, Function>; using dispatch_map_t = std::map<std::string, Function>;
class DispatchRegistry { class DispatchRegistry
public: {
dispatch_map_t escape; public:
dispatch_map_t CSI; dispatch_map_t escape;
dispatch_map_t control; dispatch_map_t CSI;
dispatch_map_t control;
DispatchRegistry() : escape(), CSI(), control() {} DispatchRegistry() : escape(), CSI(), control() {}
}; };
DispatchRegistry & get_global_dispatch_registry( void ); DispatchRegistry& get_global_dispatch_registry( void );
class Dispatcher { class Dispatcher
private: {
std::string params; private:
std::vector<int> parsed_params; std::string params;
bool parsed; std::vector<int> parsed_params;
bool parsed;
std::string dispatch_chars; std::string dispatch_chars;
std::vector<wchar_t> OSC_string; /* only used to set the window title */ std::vector<wchar_t> OSC_string; /* only used to set the window title */
void parse_params( void ); void parse_params( void );
public: public:
static const int PARAM_MAX = 65535; static const int PARAM_MAX = 65535;
/* prevent evil escape sequences from causing long loops */ /* prevent evil escape sequences from causing long loops */
std::string terminal_to_host; /* this is the reply string */ std::string terminal_to_host; /* this is the reply string */
Dispatcher(); Dispatcher();
int getparam( size_t N, int defaultval ); int getparam( size_t N, int defaultval );
int param_count( void ); int param_count( void );
void newparamchar( const Parser::Param *act ); void newparamchar( const Parser::Param* act );
void collect( const Parser::Collect *act ); void collect( const Parser::Collect* act );
void clear( const Parser::Clear *act ); void clear( const Parser::Clear* act );
std::string str( void ); std::string str( void );
void dispatch( Function_Type type, const Parser::Action *act, Framebuffer *fb ); void dispatch( Function_Type type, const Parser::Action* act, Framebuffer* fb );
std::string get_dispatch_chars( void ) const { return dispatch_chars; } std::string get_dispatch_chars( void ) const { return dispatch_chars; }
std::vector<wchar_t> get_OSC_string( void ) const { return OSC_string; } std::vector<wchar_t> get_OSC_string( void ) const { return OSC_string; }
void OSC_put( const Parser::OSC_Put *act ); void OSC_put( const Parser::OSC_Put* act );
void OSC_start( const Parser::OSC_Start *act ); void OSC_start( const Parser::OSC_Start* act );
void OSC_dispatch( const Parser::OSC_End *act, Framebuffer *fb ); void OSC_dispatch( const Parser::OSC_End* act, Framebuffer* fb );
bool operator==( const Dispatcher &x ) const; bool operator==( const Dispatcher& x ) const;
}; };
} }
#endif #endif
+142 -163
View File
@@ -32,14 +32,14 @@
#include <cstdio> #include <cstdio>
#include "terminaldisplay.h"
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
#include "terminaldisplay.h"
using namespace Terminal; using namespace Terminal;
/* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */ /* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */
static const Renditions & initial_rendition( void ) static const Renditions& initial_rendition( void )
{ {
const static Renditions blank = Renditions( 0 ); const static Renditions blank = Renditions( 0 );
return blank; return blank;
@@ -53,16 +53,16 @@ std::string Display::open() const
std::string Display::close() const std::string Display::close() const
{ {
return std::string( "\033[?1l\033[0m\033[?25h" return std::string( "\033[?1l\033[0m\033[?25h"
"\033[?1003l\033[?1002l\033[?1001l\033[?1000l" "\033[?1003l\033[?1002l\033[?1001l\033[?1000l"
"\033[?1015l\033[?1006l\033[?1005l" ) + "\033[?1015l\033[?1006l\033[?1005l" )
std::string( rmcup ? rmcup : "" ); + std::string( rmcup ? rmcup : "" );
} }
std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const std::string Display::new_frame( bool initialized, const Framebuffer& last, const Framebuffer& f ) const
{ {
FrameState frame( last ); FrameState frame( last );
char tmp[ 64 ]; char tmp[64];
/* has bell been rung? */ /* has bell been rung? */
if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) { if ( f.get_bell_count() != frame.last_frame.get_bell_count() ) {
@@ -71,69 +71,57 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
using title_type = Terminal::Framebuffer::title_type; using title_type = Terminal::Framebuffer::title_type;
/* has icon name or window title changed? */ /* has icon name or window title changed? */
if ( has_title && f.is_title_initialized() && if ( has_title && f.is_title_initialized()
( (!initialized) && ( ( !initialized ) || ( f.get_icon_name() != frame.last_frame.get_icon_name() )
|| (f.get_icon_name() != frame.last_frame.get_icon_name()) || ( f.get_window_title() != frame.last_frame.get_window_title() ) ) ) {
|| (f.get_window_title() != frame.last_frame.get_window_title()) ) ) { /* set icon name and window title */
/* set icon name and window title */
if ( f.get_icon_name() == f.get_window_title() ) { if ( f.get_icon_name() == f.get_window_title() ) {
/* write combined Icon Name and Window Title */ /* write combined Icon Name and Window Title */
frame.append( "\033]0;" ); frame.append( "\033]0;" );
const title_type &window_title( f.get_window_title() ); const title_type& window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin(); for ( title_type::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) {
i != window_title.end(); frame.append( *i );
i++ ) {
frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
/* ST is more correct, but BEL more widely supported */ /* ST is more correct, but BEL more widely supported */
} else { } else {
/* write Icon Name */ /* write Icon Name */
frame.append( "\033]1;" ); frame.append( "\033]1;" );
const title_type &icon_name( f.get_icon_name() ); const title_type& icon_name( f.get_icon_name() );
for ( title_type::const_iterator i = icon_name.begin(); for ( title_type::const_iterator i = icon_name.begin(); i != icon_name.end(); i++ ) {
i != icon_name.end(); frame.append( *i );
i++ ) {
frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
frame.append( "\033]2;" ); frame.append( "\033]2;" );
const title_type &window_title( f.get_window_title() ); const title_type& window_title( f.get_window_title() );
for ( title_type::const_iterator i = window_title.begin(); for ( title_type::const_iterator i = window_title.begin(); i != window_title.end(); i++ ) {
i != window_title.end(); frame.append( *i );
i++ ) {
frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
} }
} }
/* has clipboard changed? */ /* has clipboard changed? */
if (f.get_clipboard() != frame.last_frame.get_clipboard()) { if ( f.get_clipboard() != frame.last_frame.get_clipboard() ) {
frame.append( "\033]52;c;" ); frame.append( "\033]52;c;" );
const title_type &clipboard( f.get_clipboard() ); const title_type& clipboard( f.get_clipboard() );
for ( title_type::const_iterator i = clipboard.begin(); for ( title_type::const_iterator i = clipboard.begin(); i != clipboard.end(); i++ ) {
i != clipboard.end();
i++ ) {
frame.append( *i ); frame.append( *i );
} }
frame.append( '\007' ); frame.append( '\007' );
} }
/* has reverse video state changed? */ /* has reverse video state changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.reverse_video != frame.last_frame.ds.reverse_video ) ) {
|| (f.ds.reverse_video != frame.last_frame.ds.reverse_video) ) {
/* set reverse video */ /* set reverse video */
snprintf( tmp, 64, "\033[?5%c", (f.ds.reverse_video ? 'h' : 'l') ); snprintf( tmp, 64, "\033[?5%c", ( f.ds.reverse_video ? 'h' : 'l' ) );
frame.append( tmp ); frame.append( tmp );
} }
/* has size changed? */ /* has size changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.get_width() != frame.last_frame.ds.get_width() )
|| (f.ds.get_width() != frame.last_frame.ds.get_width()) || ( f.ds.get_height() != frame.last_frame.ds.get_height() ) ) {
|| (f.ds.get_height() != frame.last_frame.ds.get_height()) ) {
/* reset scrolling region */ /* reset scrolling region */
frame.append( "\033[r" ); frame.append( "\033[r" );
@@ -161,7 +149,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
if ( frame.last_frame.ds.get_width() < f.ds.get_width() ) { if ( frame.last_frame.ds.get_width() < f.ds.get_width() ) {
for ( Framebuffer::rows_type::iterator p = rows.begin(); p != rows.end(); p++ ) { for ( Framebuffer::rows_type::iterator p = rows.begin(); p != rows.end(); p++ ) {
*p = std::make_shared<Row>( **p ); *p = std::make_shared<Row>( **p );
(*p)->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) ); ( *p )->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) );
} }
} }
/* Add rows if we've gotten a resize and new is taller than old */ /* Add rows if we've gotten a resize and new is taller than old */
@@ -179,29 +167,26 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
int scroll_height = 0; int scroll_height = 0;
for ( int row = 0; row < f.ds.get_height(); row++ ) { for ( int row = 0; row < f.ds.get_height(); row++ ) {
const Row *new_row = f.get_row( 0 ); const Row* new_row = f.get_row( 0 );
const Row *old_row = &*rows.at( row ); const Row* old_row = &*rows.at( row );
if ( ! ( new_row == old_row || *new_row == *old_row ) ) { if ( !( new_row == old_row || *new_row == *old_row ) ) {
continue; continue;
} }
/* if row 0, we're looking at ourselves and probably didn't scroll */ /* if row 0, we're looking at ourselves and probably didn't scroll */
if ( row == 0 ) { if ( row == 0 ) {
break; break;
} }
/* found a scroll */ /* found a scroll */
lines_scrolled = row; lines_scrolled = row;
scroll_height = 1; scroll_height = 1;
/* how big is the region that was scrolled? */ /* how big is the region that was scrolled? */
for ( int region_height = 1; for ( int region_height = 1; lines_scrolled + region_height < f.ds.get_height(); region_height++ ) {
lines_scrolled + region_height < f.ds.get_height(); if ( *f.get_row( region_height ) == *rows.at( lines_scrolled + region_height ) ) {
region_height++ ) { scroll_height = region_height + 1;
if ( *f.get_row( region_height ) } else {
== *rows.at( lines_scrolled + region_height ) ) { break;
scroll_height = region_height + 1; }
} else {
break;
}
} }
break; break;
@@ -211,54 +196,52 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
frame_y = scroll_height; frame_y = scroll_height;
if ( lines_scrolled ) { if ( lines_scrolled ) {
/* Now we need a proper blank row. */ /* Now we need a proper blank row. */
if ( blank_row.get() == NULL ) { if ( blank_row.get() == NULL ) {
const size_t w = f.ds.get_width(); const size_t w = f.ds.get_width();
const color_type c = 0; const color_type c = 0;
blank_row = std::make_shared<Row>( w, c ); blank_row = std::make_shared<Row>( w, c );
} }
frame.update_rendition( initial_rendition(), true ); frame.update_rendition( initial_rendition(), true );
int top_margin = 0; int top_margin = 0;
int bottom_margin = top_margin + lines_scrolled + scroll_height - 1; int bottom_margin = top_margin + lines_scrolled + scroll_height - 1;
assert( bottom_margin < f.ds.get_height() ); assert( bottom_margin < f.ds.get_height() );
/* Common case: if we're already on the bottom line and we're scrolling the whole /* Common case: if we're already on the bottom line and we're scrolling the whole
* screen, just do a CR and LFs. * screen, just do a CR and LFs.
*/ */
if ( scroll_height + lines_scrolled == f.ds.get_height() if ( scroll_height + lines_scrolled == f.ds.get_height() && frame.cursor_y + 1 == f.ds.get_height() ) {
&& frame.cursor_y + 1 == f.ds.get_height() ) { frame.append( '\r' );
frame.append( '\r' ); frame.append( lines_scrolled, '\n' );
frame.append( lines_scrolled, '\n' ); frame.cursor_x = 0;
frame.cursor_x = 0; } else {
} else { /* set scrolling region */
/* set scrolling region */ snprintf( tmp, 64, "\033[%d;%dr", top_margin + 1, bottom_margin + 1 );
snprintf( tmp, 64, "\033[%d;%dr", frame.append( tmp );
top_margin + 1, bottom_margin + 1);
frame.append( tmp );
/* go to bottom of scrolling region */ /* go to bottom of scrolling region */
frame.cursor_x = frame.cursor_y = -1; frame.cursor_x = frame.cursor_y = -1;
frame.append_silent_move( bottom_margin, 0 ); frame.append_silent_move( bottom_margin, 0 );
/* scroll */ /* scroll */
frame.append( lines_scrolled, '\n' ); frame.append( lines_scrolled, '\n' );
/* reset scrolling region */ /* reset scrolling region */
frame.append( "\033[r" ); frame.append( "\033[r" );
/* invalidate cursor position after unsetting scrolling region */ /* invalidate cursor position after unsetting scrolling region */
frame.cursor_x = frame.cursor_y = -1; frame.cursor_x = frame.cursor_y = -1;
} }
/* do the move in our local index */ /* do the move in our local index */
for ( int i = top_margin; i <= bottom_margin; i++ ) { for ( int i = top_margin; i <= bottom_margin; i++ ) {
if ( i + lines_scrolled <= bottom_margin ) { if ( i + lines_scrolled <= bottom_margin ) {
rows.at( i ) = rows.at( i + lines_scrolled ); rows.at( i ) = rows.at( i + lines_scrolled );
} else { } else {
rows.at( i ) = blank_row; rows.at( i ) = blank_row;
} }
} }
} }
} }
} }
@@ -270,15 +253,13 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
} }
/* has cursor location changed? */ /* has cursor location changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.get_cursor_row() != frame.cursor_y )
|| (f.ds.get_cursor_row() != frame.cursor_y) || ( f.ds.get_cursor_col() != frame.cursor_x ) ) {
|| (f.ds.get_cursor_col() != frame.cursor_x) ) {
frame.append_move( f.ds.get_cursor_row(), f.ds.get_cursor_col() ); frame.append_move( f.ds.get_cursor_row(), f.ds.get_cursor_col() );
} }
/* has cursor visibility changed? */ /* has cursor visibility changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.cursor_visible != frame.cursor_visible ) ) {
|| (f.ds.cursor_visible != frame.cursor_visible) ) {
if ( f.ds.cursor_visible ) { if ( f.ds.cursor_visible ) {
frame.append( "\033[?25h" ); frame.append( "\033[?25h" );
} else { } else {
@@ -290,67 +271,68 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
frame.update_rendition( f.ds.get_renditions(), !initialized ); frame.update_rendition( f.ds.get_renditions(), !initialized );
/* has bracketed paste mode changed? */ /* has bracketed paste mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste ) ) {
|| (f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste) ) {
frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" ); frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" );
} }
/* has mouse reporting mode changed? */ /* has mouse reporting mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_reporting_mode != frame.last_frame.ds.mouse_reporting_mode ) ) {
|| (f.ds.mouse_reporting_mode != frame.last_frame.ds.mouse_reporting_mode) ) { if ( f.ds.mouse_reporting_mode == DrawState::MOUSE_REPORTING_NONE ) {
if (f.ds.mouse_reporting_mode == DrawState::MOUSE_REPORTING_NONE) { frame.append( "\033[?1003l" );
frame.append("\033[?1003l"); frame.append( "\033[?1002l" );
frame.append("\033[?1002l"); frame.append( "\033[?1001l" );
frame.append("\033[?1001l"); frame.append( "\033[?1000l" );
frame.append("\033[?1000l");
} else { } else {
if (frame.last_frame.ds.mouse_reporting_mode != DrawState::MOUSE_REPORTING_NONE) { if ( frame.last_frame.ds.mouse_reporting_mode != DrawState::MOUSE_REPORTING_NONE ) {
snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_reporting_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dl", frame.last_frame.ds.mouse_reporting_mode );
frame.append(tmp); frame.append( tmp );
} }
snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_reporting_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dh", f.ds.mouse_reporting_mode );
frame.append(tmp); frame.append( tmp );
} }
} }
/* has mouse focus mode changed? */ /* has mouse focus mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_focus_event != frame.last_frame.ds.mouse_focus_event ) ) {
|| (f.ds.mouse_focus_event != frame.last_frame.ds.mouse_focus_event) ) {
frame.append( f.ds.mouse_focus_event ? "\033[?1004h" : "\033[?1004l" ); frame.append( f.ds.mouse_focus_event ? "\033[?1004h" : "\033[?1004l" );
} }
/* has mouse encoding mode changed? */ /* has mouse encoding mode changed? */
if ( (!initialized) if ( ( !initialized ) || ( f.ds.mouse_encoding_mode != frame.last_frame.ds.mouse_encoding_mode ) ) {
|| (f.ds.mouse_encoding_mode != frame.last_frame.ds.mouse_encoding_mode) ) { if ( f.ds.mouse_encoding_mode == DrawState::MOUSE_ENCODING_DEFAULT ) {
if (f.ds.mouse_encoding_mode == DrawState::MOUSE_ENCODING_DEFAULT) { frame.append( "\033[?1015l" );
frame.append("\033[?1015l"); frame.append( "\033[?1006l" );
frame.append("\033[?1006l"); frame.append( "\033[?1005l" );
frame.append("\033[?1005l");
} else { } else {
if (frame.last_frame.ds.mouse_encoding_mode != DrawState::MOUSE_ENCODING_DEFAULT) { if ( frame.last_frame.ds.mouse_encoding_mode != DrawState::MOUSE_ENCODING_DEFAULT ) {
snprintf(tmp, sizeof(tmp), "\033[?%dl", frame.last_frame.ds.mouse_encoding_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dl", frame.last_frame.ds.mouse_encoding_mode );
frame.append(tmp); frame.append( tmp );
} }
snprintf(tmp, sizeof(tmp), "\033[?%dh", f.ds.mouse_encoding_mode); snprintf( tmp, sizeof( tmp ), "\033[?%dh", f.ds.mouse_encoding_mode );
frame.append(tmp); frame.append( tmp );
} }
} }
return frame.str; return frame.str;
} }
bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f, int frame_y, const Row &old_row, bool wrap ) const bool Display::put_row( bool initialized,
FrameState& frame,
const Framebuffer& f,
int frame_y,
const Row& old_row,
bool wrap ) const
{ {
char tmp[ 64 ]; char tmp[64];
int frame_x = 0; int frame_x = 0;
const Row &row = *f.get_row( frame_y ); const Row& row = *f.get_row( frame_y );
const Row::cells_type &cells = row.cells; const Row::cells_type& cells = row.cells;
const Row::cells_type &old_cells = old_row.cells; const Row::cells_type& old_cells = old_row.cells;
/* If we're forced to write the first column because of wrap, go ahead and do so. */ /* If we're forced to write the first column because of wrap, go ahead and do so. */
if ( wrap ) { if ( wrap ) {
const Cell &cell = cells.at( 0 ); const Cell& cell = cells.at( 0 );
frame.update_rendition( cell.get_renditions() ); frame.update_rendition( cell.get_renditions() );
frame.append_cell( cell ); frame.append_cell( cell );
frame_x += cell.get_width(); frame_x += cell.get_width();
@@ -358,7 +340,7 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
} }
/* If rows are the same object, we don't need to do anything at all. */ /* If rows are the same object, we don't need to do anything at all. */
if (initialized && &row == &old_row ) { if ( initialized && &row == &old_row ) {
return false; return false;
} }
@@ -371,12 +353,10 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
/* iterate for every cell */ /* iterate for every cell */
while ( frame_x < row_width ) { while ( frame_x < row_width ) {
const Cell &cell = cells.at( frame_x ); const Cell& cell = cells.at( frame_x );
/* Does cell need to be drawn? Skip all this. */ /* Does cell need to be drawn? Skip all this. */
if ( initialized if ( initialized && !clear_count && ( cell == old_cells.at( frame_x ) ) ) {
&& !clear_count
&& ( cell == old_cells.at( frame_x ) ) ) {
frame_x += cell.get_width(); frame_x += cell.get_width();
continue; continue;
} }
@@ -384,13 +364,13 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
/* Slurp up all the empty cells */ /* Slurp up all the empty cells */
if ( cell.empty() ) { if ( cell.empty() ) {
if ( !clear_count ) { if ( !clear_count ) {
blank_renditions = cell.get_renditions(); blank_renditions = cell.get_renditions();
} }
if ( cell.get_renditions() == blank_renditions ) { if ( cell.get_renditions() == blank_renditions ) {
/* Remember run of blank cells */ /* Remember run of blank cells */
clear_count++; clear_count++;
frame_x++; frame_x++;
continue; continue;
} }
} }
@@ -401,24 +381,23 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
frame.update_rendition( blank_renditions ); frame.update_rendition( blank_renditions );
bool can_use_erase = has_bce || ( frame.current_rendition == initial_rendition() ); bool can_use_erase = has_bce || ( frame.current_rendition == initial_rendition() );
if ( can_use_erase && has_ech && clear_count > 4 ) { if ( can_use_erase && has_ech && clear_count > 4 ) {
snprintf( tmp, 64, "\033[%dX", clear_count ); snprintf( tmp, 64, "\033[%dX", clear_count );
frame.append( tmp ); frame.append( tmp );
} else { } else {
frame.append( clear_count, ' ' ); frame.append( clear_count, ' ' );
frame.cursor_x = frame_x; frame.cursor_x = frame_x;
} }
clear_count = 0; clear_count = 0;
// If the current character is *another* empty cell in a different rendition, // If the current character is *another* empty cell in a different rendition,
// we restart counting and continue here // we restart counting and continue here
if ( cell.empty() ) { if ( cell.empty() ) {
blank_renditions = cell.get_renditions(); blank_renditions = cell.get_renditions();
clear_count = 1; clear_count = 1;
frame_x++; frame_x++;
continue; continue;
} }
} }
/* Now draw a character cell. */ /* Now draw a character cell. */
/* Move to the right position. */ /* Move to the right position. */
const int cell_width = cell.get_width(); const int cell_width = cell.get_width();
@@ -460,8 +439,7 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
} }
} }
if ( ! ( wrote_last_cell if ( !( wrote_last_cell && ( frame_y < f.ds.get_height() - 1 ) ) ) {
&& (frame_y < f.ds.get_height() - 1) ) ) {
return false; return false;
} }
/* To hint that a word-select should group the end of one line /* To hint that a word-select should group the end of one line
@@ -480,10 +458,9 @@ bool Display::put_row( bool initialized, FrameState &frame, const Framebuffer &f
return false; return false;
} }
FrameState::FrameState( const Framebuffer &s_last ) FrameState::FrameState( const Framebuffer& s_last )
: str(), cursor_x(0), cursor_y(0), current_rendition( 0 ), : str(), cursor_x( 0 ), cursor_y( 0 ), current_rendition( 0 ), cursor_visible( s_last.ds.cursor_visible ),
cursor_visible( s_last.ds.cursor_visible ), last_frame( s_last )
last_frame( s_last )
{ {
/* Preallocate for better performance. Make a guess-- doesn't matter for correctness */ /* Preallocate for better performance. Make a guess-- doesn't matter for correctness */
str.reserve( last_frame.ds.get_width() * last_frame.ds.get_height() * 4 ); str.reserve( last_frame.ds.get_width() * last_frame.ds.get_height() * 4 );
@@ -491,7 +468,8 @@ FrameState::FrameState( const Framebuffer &s_last )
void FrameState::append_silent_move( int y, int x ) void FrameState::append_silent_move( int y, int x )
{ {
if ( cursor_x == x && cursor_y == y ) return; if ( cursor_x == x && cursor_y == y )
return;
/* turn off cursor if necessary before moving cursor */ /* turn off cursor if necessary before moving cursor */
if ( cursor_visible ) { if ( cursor_visible ) {
append( "\033[?25l" ); append( "\033[?25l" );
@@ -511,7 +489,7 @@ void FrameState::append_move( int y, int x )
// Can we use CR and/or LF? They're cheap and easier to trace. // Can we use CR and/or LF? They're cheap and easier to trace.
if ( x == 0 && y - last_y >= 0 && y - last_y < 5 ) { if ( x == 0 && y - last_y >= 0 && y - last_y < 5 ) {
if ( last_x != 0 ) { if ( last_x != 0 ) {
append( '\r' ); append( '\r' );
} }
append( y - last_y, '\n' ); append( y - last_y, '\n' );
return; return;
@@ -523,13 +501,14 @@ void FrameState::append_move( int y, int x )
} }
// More optimizations are possible. // More optimizations are possible.
} }
char tmp[ 64 ]; char tmp[64];
snprintf( tmp, 64, "\033[%d;%dH", y + 1, x + 1 ); snprintf( tmp, 64, "\033[%d;%dH", y + 1, x + 1 );
append( tmp ); append( tmp );
} }
void FrameState::update_rendition(const Renditions &r, bool force) { void FrameState::update_rendition( const Renditions& r, bool force )
if ( force || !(current_rendition == r) ) { {
if ( force || !( current_rendition == r ) ) {
/* print renditions */ /* print renditions */
append_string( r.sgr() ); append_string( r.sgr() );
current_rendition = r; current_rendition = r;
+40 -33
View File
@@ -36,52 +36,59 @@
#include "src/terminal/terminalframebuffer.h" #include "src/terminal/terminalframebuffer.h"
namespace Terminal { namespace Terminal {
/* variables used within a new_frame */ /* variables used within a new_frame */
class FrameState { class FrameState
public: {
std::string str; public:
std::string str;
int cursor_x, cursor_y; int cursor_x, cursor_y;
Renditions current_rendition; Renditions current_rendition;
bool cursor_visible; bool cursor_visible;
const Framebuffer &last_frame; const Framebuffer& last_frame;
FrameState( const Framebuffer &s_last ); FrameState( const Framebuffer& s_last );
void append( char c ) { str.append( 1, c ); } void append( char c ) { str.append( 1, c ); }
void append( size_t s, char c ) { str.append( s, c ); } void append( size_t s, char c ) { str.append( s, c ); }
void append( wchar_t wc ) { Cell::append_to_str( str, wc ); } void append( wchar_t wc ) { Cell::append_to_str( str, wc ); }
void append( const char * s ) { str.append( s ); } void append( const char* s ) { str.append( s ); }
void append_string( const std::string &append ) { str.append(append); } void append_string( const std::string& append ) { str.append( append ); }
void append_cell(const Cell & cell) { cell.print_grapheme( str ); } void append_cell( const Cell& cell ) { cell.print_grapheme( str ); }
void append_silent_move( int y, int x ); void append_silent_move( int y, int x );
void append_move( int y, int x ); void append_move( int y, int x );
void update_rendition( const Renditions &r, bool force = false ); void update_rendition( const Renditions& r, bool force = false );
}; };
class Display { class Display
private: {
bool has_ech; /* erase character is part of vt200 but not supported by tmux private:
(or by "screen" terminfo entry, which is what tmux advertises) */ bool has_ech; /* erase character is part of vt200 but not supported by tmux
(or by "screen" terminfo entry, which is what tmux advertises) */
bool has_bce; /* erases result in cell filled with background color */ bool has_bce; /* erases result in cell filled with background color */
bool has_title; /* supports window title and icon name */ bool has_title; /* supports window title and icon name */
const char *smcup, *rmcup; /* enter and exit alternate screen mode */ const char *smcup, *rmcup; /* enter and exit alternate screen mode */
bool put_row( bool initialized, FrameState &frame, const Framebuffer &f, int frame_y, const Row &old_row, bool wrap ) const; bool put_row( bool initialized,
FrameState& frame,
const Framebuffer& f,
int frame_y,
const Row& old_row,
bool wrap ) const;
public: public:
std::string open() const; std::string open() const;
std::string close() const; std::string close() const;
std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const; std::string new_frame( bool initialized, const Framebuffer& last, const Framebuffer& f ) const;
Display( bool use_environment ); Display( bool use_environment );
}; };
} }
#endif #endif
+37 -41
View File
@@ -36,45 +36,45 @@
#include "src/include/config.h" #include "src/include/config.h"
#include "terminaldisplay.h" #include "terminaldisplay.h"
#include <string>
#include <stdexcept> #include <stdexcept>
#include <string>
#if defined HAVE_NCURSESW_CURSES_H #if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/curses.h> #include <ncursesw/curses.h>
# include <ncursesw/term.h> #include <ncursesw/term.h>
#elif defined HAVE_NCURSESW_H #elif defined HAVE_NCURSESW_H
# include <ncursesw.h> #include <ncursesw.h>
# include <term.h> #include <term.h>
#elif defined HAVE_NCURSES_CURSES_H #elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/curses.h> #include <ncurses/curses.h>
# include <ncurses/term.h> #include <ncurses/term.h>
#elif defined HAVE_NCURSES_H #elif defined HAVE_NCURSES_H
# include <ncurses.h> #include <ncurses.h>
# include <term.h> #include <term.h>
#elif defined HAVE_CURSES_H #elif defined HAVE_CURSES_H
# include <curses.h> #include <curses.h>
# include <term.h> #include <term.h>
#else #else
# error "SysV or X/Open-compatible Curses header file required" #error "SysV or X/Open-compatible Curses header file required"
#endif #endif
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
using namespace Terminal; using namespace Terminal;
static bool ti_flag( const char *capname ) static bool ti_flag( const char* capname )
{ {
int val = tigetflag( const_cast<char *>( capname ) ); int val = tigetflag( const_cast<char*>( capname ) );
if ( val == -1 ) { if ( val == -1 ) {
throw std::invalid_argument( std::string( "Invalid terminfo boolean capability " ) + capname ); throw std::invalid_argument( std::string( "Invalid terminfo boolean capability " ) + capname );
} }
return val; return val;
} }
static const char *ti_str( const char *capname ) static const char* ti_str( const char* capname )
{ {
const char *val = tigetstr( const_cast<char *>( capname ) ); const char* val = tigetstr( const_cast<char*>( capname ) );
if ( val == (const char *)-1 ) { if ( val == (const char*)-1 ) {
throw std::invalid_argument( std::string( "Invalid terminfo string capability " ) + capname ); throw std::invalid_argument( std::string( "Invalid terminfo string capability " ) + capname );
} }
return val; return val;
@@ -85,22 +85,22 @@ Display::Display( bool use_environment )
{ {
if ( use_environment ) { if ( use_environment ) {
int errret = -2; int errret = -2;
int ret = setupterm( (char *)0, 1, &errret ); int ret = setupterm( (char*)0, 1, &errret );
if ( ret != OK ) { if ( ret != OK ) {
switch ( errret ) { switch ( errret ) {
case 1: case 1:
throw std::runtime_error( "Terminal is hardcopy and cannot be used by curses applications." ); throw std::runtime_error( "Terminal is hardcopy and cannot be used by curses applications." );
break; break;
case 0: case 0:
throw std::runtime_error( "Unknown terminal type." ); throw std::runtime_error( "Unknown terminal type." );
break; break;
case -1: case -1:
throw std::runtime_error( "Terminfo database could not be found." ); throw std::runtime_error( "Terminfo database could not be found." );
break; break;
default: default:
throw std::runtime_error( "Unknown terminfo error." ); throw std::runtime_error( "Unknown terminfo error." );
break; break;
} }
} }
@@ -113,18 +113,14 @@ Display::Display( bool use_environment )
/* Check if we can set the window title and icon name. terminfo does not /* Check if we can set the window title and icon name. terminfo does not
have reliable information on this, so we hardcode a whitelist of have reliable information on this, so we hardcode a whitelist of
terminal type prefixes. */ terminal type prefixes. */
static const char * const title_term_types[] = { static const char* const title_term_types[]
"xterm", "rxvt", "kterm", "Eterm", "alacritty", "screen", "tmux" = { "xterm", "rxvt", "kterm", "Eterm", "alacritty", "screen", "tmux" };
};
has_title = false; has_title = false;
const char *term_type = getenv( "TERM" ); const char* term_type = getenv( "TERM" );
if ( term_type ) { if ( term_type ) {
for ( size_t i = 0; for ( size_t i = 0; i < sizeof( title_term_types ) / sizeof( const char* ); i++ ) {
i < sizeof( title_term_types ) / sizeof( const char * ); if ( 0 == strncmp( term_type, title_term_types[i], strlen( title_term_types[i] ) ) ) {
i++ ) {
if ( 0 == strncmp( term_type, title_term_types[ i ],
strlen( title_term_types[ i ] ) ) ) {
has_title = true; has_title = true;
break; break;
} }
@@ -132,8 +128,8 @@ Display::Display( bool use_environment )
} }
if ( !getenv( "MOSH_NO_TERM_INIT" ) ) { if ( !getenv( "MOSH_NO_TERM_INIT" ) ) {
smcup = ti_str("smcup"); smcup = ti_str( "smcup" );
rmcup = ti_str("rmcup"); rmcup = ti_str( "rmcup" );
} }
} }
} }
+133 -121
View File
@@ -39,11 +39,7 @@
using namespace Terminal; using namespace Terminal;
Cell::Cell( color_type background_color ) Cell::Cell( color_type background_color )
: contents(), : contents(), renditions( background_color ), wide( false ), fallback( false ), wrap( false )
renditions( background_color ),
wide( false ),
fallback( false ),
wrap( false )
{} {}
void Cell::reset( color_type background_color ) void Cell::reset( color_type background_color )
@@ -59,46 +55,44 @@ void DrawState::reinitialize_tabs( unsigned int start )
{ {
assert( default_tabs ); assert( default_tabs );
for ( unsigned int i = start; i < tabs.size(); i++ ) { for ( unsigned int i = start; i < tabs.size(); i++ ) {
tabs[ i ] = ( (i % 8) == 0 ); tabs[i] = ( ( i % 8 ) == 0 );
} }
} }
DrawState::DrawState( int s_width, int s_height ) DrawState::DrawState( int s_width, int s_height )
: width( s_width ), height( s_height ), : width( s_width ), height( s_height ), cursor_col( 0 ), cursor_row( 0 ), combining_char_col( 0 ),
cursor_col( 0 ), cursor_row( 0 ), combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_top_row( 0 ),
combining_char_col( 0 ), combining_char_row( 0 ), default_tabs( true ), tabs( s_width ), scrolling_region_bottom_row( height - 1 ), renditions( 0 ), save(), next_print_will_wrap( false ),
scrolling_region_top_row( 0 ), scrolling_region_bottom_row( height - 1 ), origin_mode( false ), auto_wrap_mode( true ), insert_mode( false ), cursor_visible( true ),
renditions( 0 ), save(), reverse_video( false ), bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ),
next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ), mouse_focus_event( false ), mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ),
insert_mode( false ), cursor_visible( true ), reverse_video( false ), application_mode_cursor_keys( false )
bracketed_paste( false ), mouse_reporting_mode( MOUSE_REPORTING_NONE ), mouse_focus_event( false ),
mouse_alternate_scroll( false ), mouse_encoding_mode( MOUSE_ENCODING_DEFAULT ), application_mode_cursor_keys( false )
{ {
reinitialize_tabs( 0 ); reinitialize_tabs( 0 );
} }
Framebuffer::Framebuffer( int s_width, int s_height ) Framebuffer::Framebuffer( int s_width, int s_height )
: rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ), ds( s_width, s_height ) : rows(), icon_name(), window_title(), clipboard(), bell_count( 0 ), title_initialized( false ),
ds( s_width, s_height )
{ {
assert( s_height > 0 ); assert( s_height > 0 );
assert( s_width > 0 ); assert( s_width > 0 );
const size_t w = s_width; const size_t w = s_width;
const color_type c = 0; const color_type c = 0;
rows = rows_type(s_height, row_pointer(std::make_shared<Row>( w, c ))); rows = rows_type( s_height, row_pointer( std::make_shared<Row>( w, c ) ) );
} }
Framebuffer::Framebuffer( const Framebuffer &other ) Framebuffer::Framebuffer( const Framebuffer& other )
: rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ), : rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ),
clipboard( other.clipboard ), bell_count( other.bell_count ), clipboard( other.clipboard ), bell_count( other.bell_count ), title_initialized( other.title_initialized ),
title_initialized( other.title_initialized ), ds( other.ds ) ds( other.ds )
{ {}
}
Framebuffer & Framebuffer::operator=( const Framebuffer &other ) Framebuffer& Framebuffer::operator=( const Framebuffer& other )
{ {
if ( this != &other ) { if ( this != &other ) {
rows = other.rows; rows = other.rows;
icon_name = other.icon_name; icon_name = other.icon_name;
window_title = other.window_title; window_title = other.window_title;
clipboard = other.clipboard; clipboard = other.clipboard;
bell_count = other.bell_count; bell_count = other.bell_count;
@@ -125,10 +119,14 @@ void DrawState::new_grapheme( void )
void DrawState::snap_cursor_to_border( void ) void DrawState::snap_cursor_to_border( void )
{ {
if ( cursor_row < limit_top() ) cursor_row = limit_top(); if ( cursor_row < limit_top() )
if ( cursor_row > limit_bottom() ) cursor_row = limit_bottom(); cursor_row = limit_top();
if ( cursor_col < 0 ) cursor_col = 0; if ( cursor_row > limit_bottom() )
if ( cursor_col >= width ) cursor_col = width - 1; cursor_row = limit_bottom();
if ( cursor_col < 0 )
cursor_col = 0;
if ( cursor_col >= width )
cursor_col = width - 1;
} }
void DrawState::move_row( int N, bool relative ) void DrawState::move_row( int N, bool relative )
@@ -157,7 +155,7 @@ void DrawState::move_col( int N, bool relative, bool implicit )
} }
if ( implicit ) { if ( implicit ) {
next_print_will_wrap = (cursor_col >= width); next_print_will_wrap = ( cursor_col >= width );
} }
snap_cursor_to_border(); snap_cursor_to_border();
@@ -170,8 +168,8 @@ void DrawState::move_col( int N, bool relative, bool implicit )
void Framebuffer::move_rows_autoscroll( int rows ) void Framebuffer::move_rows_autoscroll( int rows )
{ {
/* don't scroll if outside the scrolling region */ /* don't scroll if outside the scrolling region */
if ( (ds.get_cursor_row() < ds.get_scrolling_region_top_row()) if ( ( ds.get_cursor_row() < ds.get_scrolling_region_top_row() )
|| (ds.get_cursor_row() > ds.get_scrolling_region_bottom_row()) ) { || ( ds.get_cursor_row() > ds.get_scrolling_region_bottom_row() ) ) {
ds.move_row( rows, true ); ds.move_row( rows, true );
return; return;
} }
@@ -189,12 +187,11 @@ void Framebuffer::move_rows_autoscroll( int rows )
ds.move_row( rows, true ); ds.move_row( rows, true );
} }
Cell *Framebuffer::get_combining_cell( void ) Cell* Framebuffer::get_combining_cell( void )
{ {
if ( (ds.get_combining_char_col() < 0) if ( ( ds.get_combining_char_col() < 0 ) || ( ds.get_combining_char_row() < 0 )
|| (ds.get_combining_char_row() < 0) || ( ds.get_combining_char_col() >= ds.get_width() )
|| (ds.get_combining_char_col() >= ds.get_width()) || ( ds.get_combining_char_row() >= ds.get_height() ) ) {
|| (ds.get_combining_char_row() >= ds.get_height()) ) {
return NULL; return NULL;
} /* can happen if a resize came in between */ } /* can happen if a resize came in between */
@@ -203,26 +200,26 @@ Cell *Framebuffer::get_combining_cell( void )
void DrawState::set_tab( void ) void DrawState::set_tab( void )
{ {
tabs[ cursor_col ] = true; tabs[cursor_col] = true;
} }
void DrawState::clear_tab( int col ) void DrawState::clear_tab( int col )
{ {
tabs[ col ] = false; tabs[col] = false;
} }
int DrawState::get_next_tab( int count ) const int DrawState::get_next_tab( int count ) const
{ {
if ( count >= 0 ) { if ( count >= 0 ) {
for ( int i = cursor_col + 1; i < width; i++ ) { for ( int i = cursor_col + 1; i < width; i++ ) {
if ( tabs[ i ] && --count == 0 ) { if ( tabs[i] && --count == 0 ) {
return i; return i;
} }
} }
return -1; return -1;
} }
for ( int i = cursor_col - 1; i > 0; i-- ) { for ( int i = cursor_col - 1; i > 0; i-- ) {
if ( tabs[ i ] && ++count == 0 ) { if ( tabs[i] && ++count == 0 ) {
return i; return i;
} }
} }
@@ -238,8 +235,10 @@ void DrawState::set_scrolling_region( int top, int bottom )
scrolling_region_top_row = top; scrolling_region_top_row = top;
scrolling_region_bottom_row = bottom; scrolling_region_bottom_row = bottom;
if ( scrolling_region_top_row < 0 ) scrolling_region_top_row = 0; if ( scrolling_region_top_row < 0 )
if ( scrolling_region_bottom_row >= height ) scrolling_region_bottom_row = height - 1; scrolling_region_top_row = 0;
if ( scrolling_region_bottom_row >= height )
scrolling_region_bottom_row = height - 1;
if ( scrolling_region_bottom_row < scrolling_region_top_row ) if ( scrolling_region_bottom_row < scrolling_region_top_row )
scrolling_region_bottom_row = scrolling_region_top_row; scrolling_region_bottom_row = scrolling_region_top_row;
@@ -261,19 +260,16 @@ int DrawState::limit_bottom( void ) const
return origin_mode ? scrolling_region_bottom_row : height - 1; return origin_mode ? scrolling_region_bottom_row : height - 1;
} }
void Framebuffer::apply_renditions_to_cell( Cell *cell ) void Framebuffer::apply_renditions_to_cell( Cell* cell )
{ {
if (!cell) { if ( !cell ) {
cell = get_mutable_cell(); cell = get_mutable_cell();
} }
cell->set_renditions( ds.get_renditions() ); cell->set_renditions( ds.get_renditions() );
} }
SavedCursor::SavedCursor() SavedCursor::SavedCursor()
: cursor_col( 0 ), cursor_row( 0 ), : cursor_col( 0 ), cursor_row( 0 ), renditions( 0 ), auto_wrap_mode( true ), origin_mode( false )
renditions( 0 ),
auto_wrap_mode( true ),
origin_mode( false )
{} {}
void DrawState::save_cursor( void ) void DrawState::save_cursor( void )
@@ -299,8 +295,8 @@ void DrawState::restore_cursor( void )
void Framebuffer::insert_line( int before_row, int count ) void Framebuffer::insert_line( int before_row, int count )
{ {
if ( (before_row < ds.get_scrolling_region_top_row()) if ( ( before_row < ds.get_scrolling_region_top_row() )
|| (before_row > ds.get_scrolling_region_bottom_row() + 1) ) { || ( before_row > ds.get_scrolling_region_bottom_row() + 1 ) ) {
return; return;
} }
@@ -318,13 +314,12 @@ void Framebuffer::insert_line( int before_row, int count )
rows.erase( start, start + scroll ); rows.erase( start, start + scroll );
// insert new rows // insert new rows
start = rows.begin() + before_row; start = rows.begin() + before_row;
rows.insert( start, scroll, newrow()); rows.insert( start, scroll, newrow() );
} }
void Framebuffer::delete_line( int row, int count ) void Framebuffer::delete_line( int row, int count )
{ {
if ( (row < ds.get_scrolling_region_top_row()) if ( ( row < ds.get_scrolling_region_top_row() ) || ( row > ds.get_scrolling_region_bottom_row() ) ) {
|| (row > ds.get_scrolling_region_bottom_row()) ) {
return; return;
} }
@@ -342,7 +337,7 @@ void Framebuffer::delete_line( int row, int count )
rows.erase( start, start + scroll ); rows.erase( start, start + scroll );
// insert a block of dummy rows // insert a block of dummy rows
start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll; start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - scroll;
rows.insert( start, scroll, newrow()); rows.insert( start, scroll, newrow() );
} }
Row::Row( const size_t s_width, const color_type background_color ) Row::Row( const size_t s_width, const color_type background_color )
@@ -407,26 +402,23 @@ void Framebuffer::resize( int s_width, int s_height )
int oldwidth = ds.get_width(); int oldwidth = ds.get_width();
ds.resize( s_width, s_height ); ds.resize( s_width, s_height );
row_pointer blankrow( newrow()); row_pointer blankrow( newrow() );
if ( oldheight != s_height ) { if ( oldheight != s_height ) {
rows.resize( s_height, blankrow ); rows.resize( s_height, blankrow );
} }
if (oldwidth == s_width) { if ( oldwidth == s_width ) {
return; return;
} }
for ( rows_type::iterator i = rows.begin(); for ( rows_type::iterator i = rows.begin(); i != rows.end() && *i != blankrow; i++ ) {
i != rows.end() && *i != blankrow;
i++ ) {
*i = std::make_shared<Row>( **i ); *i = std::make_shared<Row>( **i );
(*i)->set_wrap( false ); ( *i )->set_wrap( false );
(*i)->cells.resize( s_width, Cell( ds.get_background_rendition() ) ); ( *i )->cells.resize( s_width, Cell( ds.get_background_rendition() ) );
} }
} }
void DrawState::resize( int s_width, int s_height ) void DrawState::resize( int s_width, int s_height )
{ {
if ( (width != s_width) if ( ( width != s_width ) || ( height != s_height ) ) {
|| (height != s_height) ) {
/* reset entire scrolling region on any resize */ /* reset entire scrolling region on any resize */
/* xterm and rxvt-unicode do this. gnome-terminal only /* xterm and rxvt-unicode do this. gnome-terminal only
resets scrolling region if it has to become smaller in resize */ resets scrolling region if it has to become smaller in resize */
@@ -447,15 +439,13 @@ void DrawState::resize( int s_width, int s_height )
/* saved cursor will be snapped to border on restore */ /* saved cursor will be snapped to border on restore */
/* invalidate combining char cell if necessary */ /* invalidate combining char cell if necessary */
if ( (combining_char_col >= width) if ( ( combining_char_col >= width ) || ( combining_char_row >= height ) ) {
|| (combining_char_row >= height) ) {
combining_char_col = combining_char_row = -1; combining_char_col = combining_char_row = -1;
} }
} }
Renditions::Renditions( color_type s_background ) Renditions::Renditions( color_type s_background )
: foreground_color( 0 ), background_color( s_background ), : foreground_color( 0 ), background_color( s_background ), attributes( 0 )
attributes( 0 )
{} {}
/* This routine cannot be used to set a color beyond the 16-color set. */ /* This routine cannot be used to set a color beyond the 16-color set. */
@@ -475,35 +465,54 @@ void Renditions::set_rendition( color_type num )
return; return;
} }
if ( (30 <= num) && (num <= 37) ) { /* foreground color in 8-color set */ if ( ( 30 <= num ) && ( num <= 37 ) ) { /* foreground color in 8-color set */
foreground_color = num; foreground_color = num;
return; return;
} else if ( (40 <= num) && (num <= 47) ) { /* background color in 8-color set */ } else if ( ( 40 <= num ) && ( num <= 47 ) ) { /* background color in 8-color set */
background_color = num; background_color = num;
return; return;
} else if ( (90 <= num) && (num <= 97) ) { /* foreground color in 16-color set */ } else if ( ( 90 <= num ) && ( num <= 97 ) ) { /* foreground color in 16-color set */
foreground_color = num - 90 + 38; foreground_color = num - 90 + 38;
return; return;
} else if ( (100 <= num) && (num <= 107) ) { /* background color in 16-color set */ } else if ( ( 100 <= num ) && ( num <= 107 ) ) { /* background color in 16-color set */
background_color = num - 100 + 48; background_color = num - 100 + 48;
return; return;
} }
bool value = num < 9; bool value = num < 9;
switch ( num ) { switch ( num ) {
case 1: case 22: set_attribute(bold, value); break; case 1:
case 3: case 23: set_attribute(italic, value); break; case 22:
case 4: case 24: set_attribute(underlined, value); break; set_attribute( bold, value );
case 5: case 25: set_attribute(blink, value); break; break;
case 7: case 27: set_attribute(inverse, value); break; case 3:
case 8: case 28: set_attribute(invisible, value); break; case 23:
default: break; /* ignore unknown rendition */ set_attribute( italic, value );
break;
case 4:
case 24:
set_attribute( underlined, value );
break;
case 5:
case 25:
set_attribute( blink, value );
break;
case 7:
case 27:
set_attribute( inverse, value );
break;
case 8:
case 28:
set_attribute( invisible, value );
break;
default:
break; /* ignore unknown rendition */
} }
} }
void Renditions::set_foreground_color( int num ) void Renditions::set_foreground_color( int num )
{ {
if ( (0 <= num) && (num <= 255) ) { if ( ( 0 <= num ) && ( num <= 255 ) ) {
foreground_color = 30 + num; foreground_color = 30 + num;
} else if ( is_true_color( num ) ) { } else if ( is_true_color( num ) ) {
foreground_color = num; foreground_color = num;
@@ -512,7 +521,7 @@ void Renditions::set_foreground_color( int num )
void Renditions::set_background_color( int num ) void Renditions::set_background_color( int num )
{ {
if ( (0 <= num) && (num <= 255) ) { if ( ( 0 <= num ) && ( num <= 255 ) ) {
background_color = 40 + num; background_color = 40 + num;
} else if ( is_true_color( num ) ) { } else if ( is_true_color( num ) ) {
background_color = num; background_color = num;
@@ -525,12 +534,18 @@ std::string Renditions::sgr( void ) const
char col[64]; char col[64];
ret.append( "\033[0" ); ret.append( "\033[0" );
if ( get_attribute( bold ) ) ret.append( ";1" ); if ( get_attribute( bold ) )
if ( get_attribute( italic ) ) ret.append( ";3" ); ret.append( ";1" );
if ( get_attribute( underlined ) ) ret.append( ";4" ); if ( get_attribute( italic ) )
if ( get_attribute( blink ) ) ret.append( ";5" ); ret.append( ";3" );
if ( get_attribute( inverse ) ) ret.append( ";7" ); if ( get_attribute( underlined ) )
if ( get_attribute( invisible ) ) ret.append( ";8" ); ret.append( ";4" );
if ( get_attribute( blink ) )
ret.append( ";5" );
if ( get_attribute( inverse ) )
ret.append( ";7" );
if ( get_attribute( invisible ) )
ret.append( ";8" );
if ( foreground_color ) { if ( foreground_color ) {
// Since foreground_color is a 25-bit field, it is promoted to an int when // Since foreground_color is a 25-bit field, it is promoted to an int when
@@ -538,10 +553,12 @@ std::string Renditions::sgr( void ) const
// https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct
// printf format specifier is thus %d. // printf format specifier is thus %d.
if ( is_true_color( foreground_color ) ) { if ( is_true_color( foreground_color ) ) {
snprintf( col, sizeof( col ), ";38;2;%d;%d;%d", snprintf( col,
(foreground_color >> 16) & 0xff, sizeof( col ),
(foreground_color >> 8) & 0xff, ";38;2;%d;%d;%d",
foreground_color & 0xff); ( foreground_color >> 16 ) & 0xff,
( foreground_color >> 8 ) & 0xff,
foreground_color & 0xff );
} else if ( foreground_color > 37 ) { /* use 256-color set */ } else if ( foreground_color > 37 ) { /* use 256-color set */
snprintf( col, sizeof( col ), ";38;5;%d", foreground_color - 30 ); snprintf( col, sizeof( col ), ";38;5;%d", foreground_color - 30 );
} else { /* ANSI foreground color */ } else { /* ANSI foreground color */
@@ -557,10 +574,12 @@ std::string Renditions::sgr( void ) const
if ( background_color ) { if ( background_color ) {
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
if ( is_true_color( background_color ) ) { if ( is_true_color( background_color ) ) {
snprintf( col, sizeof( col ), ";48;2;%d;%d;%d", snprintf( col,
(background_color >> 16) & 0xff, sizeof( col ),
(background_color >> 8) & 0xff, ";48;2;%d;%d;%d",
background_color & 0xff); ( background_color >> 16 ) & 0xff,
( background_color >> 8 ) & 0xff,
background_color & 0xff );
} else if ( background_color > 47 ) { /* use 256-color set */ } else if ( background_color > 47 ) { /* use 256-color set */
snprintf( col, sizeof( col ), ";48;5;%d", background_color - 40 ); snprintf( col, sizeof( col ), ";48;5;%d", background_color - 40 );
} else { /* ANSI background color */ } else { /* ANSI background color */
@@ -578,20 +597,18 @@ std::string Renditions::sgr( void ) const
void Row::reset( color_type background_color ) void Row::reset( color_type background_color )
{ {
gen = get_gen(); gen = get_gen();
for ( cells_type::iterator i = cells.begin(); for ( cells_type::iterator i = cells.begin(); i != cells.end(); i++ ) {
i != cells.end();
i++ ) {
i->reset( background_color ); i->reset( background_color );
} }
} }
void Framebuffer::prefix_window_title( const title_type &s ) void Framebuffer::prefix_window_title( const title_type& s )
{ {
if ( icon_name == window_title ) { if ( icon_name == window_title ) {
/* preserve equivalence */ /* preserve equivalence */
icon_name.insert(icon_name.begin(), s.begin(), s.end() ); icon_name.insert( icon_name.begin(), s.begin(), s.end() );
} }
window_title.insert(window_title.begin(), s.begin(), s.end() ); window_title.insert( window_title.begin(), s.begin(), s.end() );
} }
std::string Cell::debug_contents( void ) const std::string Cell::debug_contents( void ) const
@@ -602,13 +619,11 @@ std::string Cell::debug_contents( void ) const
std::string chars( 1, '\'' ); std::string chars( 1, '\'' );
print_grapheme( chars ); print_grapheme( chars );
chars.append( "' [" ); chars.append( "' [" );
const char *lazycomma = ""; const char* lazycomma = "";
char buf[64]; char buf[64];
for ( content_type::const_iterator i = contents.begin(); for ( content_type::const_iterator i = contents.begin(); i < contents.end(); i++ ) {
i < contents.end();
i++ ) {
snprintf( buf, sizeof buf, "%s0x%02x", lazycomma, static_cast<uint8_t>(*i) ); snprintf( buf, sizeof buf, "%s0x%02x", lazycomma, static_cast<uint8_t>( *i ) );
chars.append( buf ); chars.append( buf );
lazycomma = ", "; lazycomma = ", ";
} }
@@ -616,7 +631,7 @@ std::string Cell::debug_contents( void ) const
return chars; return chars;
} }
bool Cell::compare( const Cell &other ) const bool Cell::compare( const Cell& other ) const
{ {
bool ret = false; bool ret = false;
@@ -627,17 +642,17 @@ bool Cell::compare( const Cell &other ) const
if ( grapheme != other_grapheme ) { if ( grapheme != other_grapheme ) {
ret = true; ret = true;
fprintf( stderr, "Graphemes: '%s' vs. '%s'\n", fprintf( stderr, "Graphemes: '%s' vs. '%s'\n", grapheme.c_str(), other_grapheme.c_str() );
grapheme.c_str(), other_grapheme.c_str() );
} }
if ( !contents_match( other ) ) { if ( !contents_match( other ) ) {
// ret = true; // ret = true;
fprintf( stderr, "Contents: %s (%ld) vs. %s (%ld)\n", fprintf( stderr,
debug_contents().c_str(), "Contents: %s (%ld) vs. %s (%ld)\n",
static_cast<long int>( contents.size() ), debug_contents().c_str(),
other.debug_contents().c_str(), static_cast<long int>( contents.size() ),
static_cast<long int>( other.contents.size() ) ); other.debug_contents().c_str(),
static_cast<long int>( other.contents.size() ) );
} }
if ( fallback != other.fallback ) { if ( fallback != other.fallback ) {
@@ -646,18 +661,16 @@ bool Cell::compare( const Cell &other ) const
// manipulated. (See [conv.prom] in various C++ standards, e.g., // manipulated. (See [conv.prom] in various C++ standards, e.g.,
// https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct // https://timsong-cpp.github.io/cppwp/n4659/conv.prom#5.) The correct
// printf format specifier is thus %d. // printf format specifier is thus %d.
fprintf( stderr, "fallback: %d vs. %d\n", fprintf( stderr, "fallback: %d vs. %d\n", fallback, other.fallback );
fallback, other.fallback );
} }
if ( wide != other.wide ) { if ( wide != other.wide ) {
ret = true; ret = true;
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
fprintf( stderr, "width: %d vs. %d\n", fprintf( stderr, "width: %d vs. %d\n", wide, other.wide );
wide, other.wide );
} }
if ( !(renditions == other.renditions) ) { if ( !( renditions == other.renditions ) ) {
ret = true; ret = true;
fprintf( stderr, "renditions differ\n" ); fprintf( stderr, "renditions differ\n" );
} }
@@ -665,8 +678,7 @@ bool Cell::compare( const Cell &other ) const
if ( wrap != other.wrap ) { if ( wrap != other.wrap ) {
ret = true; ret = true;
// See comment above about bit-field promotion; it applies here as well. // See comment above about bit-field promotion; it applies here as well.
fprintf( stderr, "wrap: %d vs. %d\n", fprintf( stderr, "wrap: %d vs. %d\n", wrap, other.wrap );
wrap, other.wrap );
} }
return ret; return ret;
+409 -395
View File
@@ -45,429 +45,443 @@
/* Terminal framebuffer */ /* Terminal framebuffer */
namespace Terminal { namespace Terminal {
using color_type = uint32_t; using color_type = uint32_t;
class Renditions { class Renditions
public: {
typedef enum { bold, faint, italic, underlined, blink, inverse, invisible, SIZE } attribute_type; public:
typedef enum
{
bold,
faint,
italic,
underlined,
blink,
inverse,
invisible,
SIZE
} attribute_type;
private: private:
static const uint64_t true_color_mask = 0x1000000; static const uint64_t true_color_mask = 0x1000000;
uint64_t foreground_color : 25; uint64_t foreground_color : 25;
uint64_t background_color : 25; uint64_t background_color : 25;
uint64_t attributes : 8; uint64_t attributes : 8;
public: public:
Renditions( color_type s_background ); Renditions( color_type s_background );
void set_foreground_color( int num ); void set_foreground_color( int num );
void set_background_color( int num ); void set_background_color( int num );
void set_rendition( color_type num ); void set_rendition( color_type num );
std::string sgr( void ) const; std::string sgr( void ) const;
static unsigned int make_true_color( unsigned int r, unsigned int g, unsigned int b ) { static unsigned int make_true_color( unsigned int r, unsigned int g, unsigned int b )
return true_color_mask | (r << 16) | (g << 8) | b; {
return true_color_mask | ( r << 16 ) | ( g << 8 ) | b;
}
static bool is_true_color( unsigned int color ) { return ( color & true_color_mask ) != 0; }
// unsigned int get_foreground_rendition() const { return foreground_color; }
unsigned int get_background_rendition() const { return background_color; }
bool operator==( const Renditions& x ) const
{
return ( attributes == x.attributes ) && ( foreground_color == x.foreground_color )
&& ( background_color == x.background_color );
}
void set_attribute( attribute_type attr, bool val )
{
attributes = val ? ( attributes | ( 1 << attr ) ) : ( attributes & ~( 1 << attr ) );
}
bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); }
void clear_attributes() { attributes = 0; }
};
class Cell
{
private:
typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */
content_type contents;
Renditions renditions;
unsigned int wide : 1; /* 0 = narrow, 1 = wide */
unsigned int fallback : 1; /* first character is combining character */
unsigned int wrap : 1;
private:
Cell();
public:
Cell( color_type background_color );
void reset( color_type background_color );
bool operator==( const Cell& x ) const
{
return ( ( contents == x.contents ) && ( fallback == x.fallback ) && ( wide == x.wide )
&& ( renditions == x.renditions ) && ( wrap == x.wrap ) );
}
bool operator!=( const Cell& x ) const { return !operator==( x ); }
/* Accessors for contents field */
std::string debug_contents( void ) const;
bool empty( void ) const { return contents.empty(); }
/* 32 seems like a reasonable limit on combining characters */
bool full( void ) const { return contents.size() >= 32; }
void clear( void ) { contents.clear(); }
bool is_blank( void ) const
{
// XXX fix.
return ( contents.empty() || contents == " " || contents == "\xC2\xA0" );
}
bool contents_match( const Cell& other ) const
{
return ( is_blank() && other.is_blank() ) || ( contents == other.contents );
}
bool compare( const Cell& other ) const;
// Is this a printing ISO 8859-1 character?
static bool isprint_iso8859_1( const wchar_t c )
{
return ( c <= 0xff && c >= 0xa0 ) || ( c <= 0x7e && c >= 0x20 );
}
static void append_to_str( std::string& dest, const wchar_t c )
{
/* ASCII? Cheat. */
if ( static_cast<uint32_t>( c ) <= 0x7f ) {
dest.push_back( static_cast<char>( c ) );
return;
} }
static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb( NULL, 0, &ps );
(void)ignore;
size_t len = wcrtomb( tmp, c, &ps );
dest.append( tmp, len );
}
static bool is_true_color(unsigned int color) { void append( const wchar_t c )
return (color & true_color_mask) != 0; {
/* ASCII? Cheat. */
if ( static_cast<uint32_t>( c ) <= 0x7f ) {
contents.push_back( static_cast<char>( c ) );
return;
} }
static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb( NULL, 0, &ps );
(void)ignore;
size_t len = wcrtomb( tmp, c, &ps );
contents.insert( contents.end(), tmp, tmp + len );
}
// unsigned int get_foreground_rendition() const { return foreground_color; } void print_grapheme( std::string& output ) const
unsigned int get_background_rendition() const { return background_color; } {
if ( contents.empty() ) {
bool operator==( const Renditions &x ) const output.append( 1, ' ' );
{ return;
return ( attributes == x.attributes )
&& ( foreground_color == x.foreground_color )
&& ( background_color == x.background_color );
} }
void set_attribute( attribute_type attr, bool val ) /*
{ * cells that begin with combining character get combiner
attributes = val ? * attached to no-break space
( attributes | (1 << attr) ) : */
( attributes & ~(1 << attr) ); if ( fallback ) {
output.append( "\xC2\xA0" );
} }
bool get_attribute( attribute_type attr ) const { return attributes & ( 1 << attr ); } output.append( contents );
void clear_attributes() { attributes = 0; } }
};
class Cell { /* Other accessors */
private: const Renditions& get_renditions( void ) const { return renditions; }
typedef std::string content_type; /* can be std::string, std::vector<uint8_t>, or __gnu_cxx::__vstring */ Renditions& get_renditions( void ) { return renditions; }
content_type contents; void set_renditions( const Renditions& r ) { renditions = r; }
Renditions renditions; bool get_wide( void ) const { return wide; }
unsigned int wide : 1; /* 0 = narrow, 1 = wide */ void set_wide( bool w ) { wide = w; }
unsigned int fallback : 1; /* first character is combining character */ unsigned int get_width( void ) const { return wide + 1; }
unsigned int wrap : 1; bool get_fallback( void ) const { return fallback; }
void set_fallback( bool f ) { fallback = f; }
bool get_wrap( void ) const { return wrap; }
void set_wrap( bool f ) { wrap = f; }
};
private: class Row
Cell(); {
public: public:
Cell( color_type background_color ); typedef std::vector<Cell> cells_type;
cells_type cells;
// gen is a generation counter. It can be used to quickly rule
// out the possibility of two rows being identical; this is useful
// in scrolling.
uint64_t gen;
void reset( color_type background_color ); private:
Row();
bool operator==( const Cell &x ) const public:
{ Row( const size_t s_width, const color_type background_color );
return ( (contents == x.contents)
&& (fallback == x.fallback) void insert_cell( int col, color_type background_color );
&& (wide == x.wide) void delete_cell( int col, color_type background_color );
&& (renditions == x.renditions)
&& (wrap == x.wrap) ); void reset( color_type background_color );
bool operator==( const Row& x ) const { return ( gen == x.gen && cells == x.cells ); }
bool get_wrap( void ) const { return cells.back().get_wrap(); }
void set_wrap( bool w ) { cells.back().set_wrap( w ); }
uint64_t get_gen() const;
};
class SavedCursor
{
public:
int cursor_col, cursor_row;
Renditions renditions;
/* not implemented: character set shift state */
bool auto_wrap_mode;
bool origin_mode;
/* not implemented: state of selective erase */
SavedCursor();
};
class DrawState
{
private:
int width, height;
void new_grapheme( void );
void snap_cursor_to_border( void );
int cursor_col, cursor_row;
int combining_char_col, combining_char_row;
bool default_tabs;
std::vector<bool> tabs;
void reinitialize_tabs( unsigned int start );
int scrolling_region_top_row, scrolling_region_bottom_row;
Renditions renditions;
SavedCursor save;
public:
bool next_print_will_wrap;
bool origin_mode;
bool auto_wrap_mode;
bool insert_mode;
bool cursor_visible;
bool reverse_video;
bool bracketed_paste;
enum MouseReportingMode
{
MOUSE_REPORTING_NONE = 0,
MOUSE_REPORTING_X10 = 9,
MOUSE_REPORTING_VT220 = 1000,
MOUSE_REPORTING_VT220_HILIGHT = 1001,
MOUSE_REPORTING_BTN_EVENT = 1002,
MOUSE_REPORTING_ANY_EVENT = 1003
} mouse_reporting_mode;
bool mouse_focus_event; // 1004
bool mouse_alternate_scroll; // 1007
enum MouseEncodingMode
{
MOUSE_ENCODING_DEFAULT = 0,
MOUSE_ENCODING_UTF8 = 1005,
MOUSE_ENCODING_SGR = 1006,
MOUSE_ENCODING_URXVT = 1015
} mouse_encoding_mode;
bool application_mode_cursor_keys;
/* bold, etc. */
void move_row( int N, bool relative = false );
void move_col( int N, bool relative = false, bool implicit = false );
int get_cursor_col( void ) const { return cursor_col; }
int get_cursor_row( void ) const { return cursor_row; }
int get_combining_char_col( void ) const { return combining_char_col; }
int get_combining_char_row( void ) const { return combining_char_row; }
int get_width( void ) const { return width; }
int get_height( void ) const { return height; }
void set_tab( void );
void clear_tab( int col );
void clear_default_tabs( void ) { default_tabs = false; }
/* Default tabs can't be restored without resetting the draw state. */
int get_next_tab( int count ) const;
void set_scrolling_region( int top, int bottom );
int get_scrolling_region_top_row( void ) const { return scrolling_region_top_row; }
int get_scrolling_region_bottom_row( void ) const { return scrolling_region_bottom_row; }
int limit_top( void ) const;
int limit_bottom( void ) const;
void set_foreground_color( int x ) { renditions.set_foreground_color( x ); }
void set_background_color( int x ) { renditions.set_background_color( x ); }
void add_rendition( color_type x ) { renditions.set_rendition( x ); }
const Renditions& get_renditions( void ) const { return renditions; }
Renditions& get_renditions( void ) { return renditions; }
int get_background_rendition( void ) const { return renditions.get_background_rendition(); }
void save_cursor( void );
void restore_cursor( void );
void clear_saved_cursor( void ) { save = SavedCursor(); }
void resize( int s_width, int s_height );
DrawState( int s_width, int s_height );
bool operator==( const DrawState& x ) const
{
/* only compare fields that affect display */
return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible )
&& ( reverse_video == x.reverse_video ) && ( renditions == x.renditions )
&& ( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode )
&& ( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll )
&& ( mouse_encoding_mode == x.mouse_encoding_mode );
}
};
class Framebuffer
{
// To minimize copying of rows and cells, we use shared_ptr to
// share unchanged rows between multiple Framebuffers. If we
// write to a row in a Framebuffer and it is shared with other
// owners, we copy it first. The shared_ptr naturally manages the
// usage of the actual rows themselves.
//
// We gain a couple of free extras by doing this:
//
// * A quick check for equality between rows in different
// Framebuffers is to simply compare the pointer values. If they
// are equal, then the rows are obviously identical.
// * If no row is shared, the frame has not been modified.
public:
typedef std::vector<wchar_t> title_type;
typedef std::shared_ptr<Row> row_pointer;
typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */
private:
rows_type rows;
title_type icon_name;
title_type window_title;
title_type clipboard;
unsigned int bell_count;
bool title_initialized; /* true if the window title has been set via an OSC */
row_pointer newrow( void )
{
const size_t w = ds.get_width();
const color_type c = ds.get_background_rendition();
return std::make_shared<Row>( w, c );
}
public:
Framebuffer( int s_width, int s_height );
Framebuffer( const Framebuffer& other );
Framebuffer& operator=( const Framebuffer& other );
DrawState ds;
const rows_type& get_rows() const { return rows; }
void scroll( int N );
void move_rows_autoscroll( int rows );
inline const Row* get_row( int row ) const
{
if ( row == -1 )
row = ds.get_cursor_row();
return rows.at( row ).get();
}
inline const Cell* get_cell( int row = -1, int col = -1 ) const
{
if ( row == -1 )
row = ds.get_cursor_row();
if ( col == -1 )
col = ds.get_cursor_col();
return &rows.at( row )->cells.at( col );
}
Row* get_mutable_row( int row )
{
if ( row == -1 )
row = ds.get_cursor_row();
row_pointer& mutable_row = rows.at( row );
// If the row is shared, copy it.
if ( !mutable_row.unique() ) {
mutable_row = std::make_shared<Row>( *mutable_row );
} }
return mutable_row.get();
}
bool operator!=( const Cell &x ) const { return !operator==( x ); } Cell* get_mutable_cell( int row = -1, int col = -1 )
{
if ( row == -1 )
row = ds.get_cursor_row();
if ( col == -1 )
col = ds.get_cursor_col();
/* Accessors for contents field */ return &get_mutable_row( row )->cells.at( col );
std::string debug_contents( void ) const; }
bool empty( void ) const { return contents.empty(); } Cell* get_combining_cell( void );
/* 32 seems like a reasonable limit on combining characters */
bool full( void ) const { return contents.size() >= 32; }
void clear( void ) { contents.clear(); }
bool is_blank( void ) const void apply_renditions_to_cell( Cell* cell );
{
// XXX fix.
return ( contents.empty()
|| contents == " "
|| contents == "\xC2\xA0" );
}
bool contents_match ( const Cell &other ) const void insert_line( int before_row, int count );
{ void delete_line( int row, int count );
return ( is_blank() && other.is_blank() )
|| ( contents == other.contents );
}
bool compare( const Cell &other ) const; void insert_cell( int row, int col );
void delete_cell( int row, int col );
// Is this a printing ISO 8859-1 character? void reset( void );
static bool isprint_iso8859_1( const wchar_t c ) void soft_reset( void );
{
return ( c <= 0xff && c >= 0xa0 ) || ( c <= 0x7e && c >= 0x20 );
}
static void append_to_str( std::string &dest, const wchar_t c ) void set_title_initialized( void ) { title_initialized = true; }
{ bool is_title_initialized( void ) const { return title_initialized; }
/* ASCII? Cheat. */ void set_icon_name( const title_type& s ) { icon_name = s; }
if ( static_cast<uint32_t>(c) <= 0x7f ) { void set_window_title( const title_type& s ) { window_title = s; }
dest.push_back( static_cast<char>(c) ); void set_clipboard( const title_type& s ) { clipboard = s; }
return; const title_type& get_icon_name( void ) const { return icon_name; }
} const title_type& get_window_title( void ) const { return window_title; }
static mbstate_t ps = mbstate_t(); const title_type& get_clipboard( void ) const { return clipboard; }
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps);
(void)ignore;
size_t len = wcrtomb(tmp, c, &ps);
dest.append( tmp, len );
}
void append( const wchar_t c ) void prefix_window_title( const title_type& s );
{
/* ASCII? Cheat. */
if ( static_cast<uint32_t>(c) <= 0x7f ) {
contents.push_back( static_cast<char>(c) );
return;
}
static mbstate_t ps = mbstate_t();
char tmp[MB_LEN_MAX];
size_t ignore = wcrtomb(NULL, 0, &ps);
(void)ignore;
size_t len = wcrtomb(tmp, c, &ps);
contents.insert( contents.end(), tmp, tmp+len );
}
void print_grapheme( std::string &output ) const void resize( int s_width, int s_height );
{
if ( contents.empty() ) {
output.append( 1, ' ' );
return;
}
/*
* cells that begin with combining character get combiner
* attached to no-break space
*/
if ( fallback ) {
output.append( "\xC2\xA0" );
}
output.append( contents );
}
/* Other accessors */ void reset_cell( Cell* c ) { c->reset( ds.get_background_rendition() ); }
const Renditions& get_renditions( void ) const { return renditions; } void reset_row( Row* r ) { r->reset( ds.get_background_rendition() ); }
Renditions& get_renditions( void ) { return renditions; }
void set_renditions( const Renditions& r ) { renditions = r; }
bool get_wide( void ) const { return wide; }
void set_wide( bool w ) { wide = w; }
unsigned int get_width( void ) const { return wide + 1; }
bool get_fallback( void ) const { return fallback; }
void set_fallback( bool f ) { fallback = f; }
bool get_wrap( void ) const { return wrap; }
void set_wrap( bool f ) { wrap = f; }
};
class Row { void ring_bell( void ) { bell_count++; }
public: unsigned int get_bell_count( void ) const { return bell_count; }
typedef std::vector<Cell> cells_type;
cells_type cells;
// gen is a generation counter. It can be used to quickly rule
// out the possibility of two rows being identical; this is useful
// in scrolling.
uint64_t gen;
private: bool operator==( const Framebuffer& x ) const
Row(); {
public: return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard )
Row( const size_t s_width, const color_type background_color ); && ( bell_count == x.bell_count ) && ( ds == x.ds );
}
void insert_cell( int col, color_type background_color ); };
void delete_cell( int col, color_type background_color );
void reset( color_type background_color );
bool operator==( const Row &x ) const
{
return ( gen == x.gen && cells == x.cells );
}
bool get_wrap( void ) const { return cells.back().get_wrap(); }
void set_wrap( bool w ) { cells.back().set_wrap( w ); }
uint64_t get_gen() const;
};
class SavedCursor {
public:
int cursor_col, cursor_row;
Renditions renditions;
/* not implemented: character set shift state */
bool auto_wrap_mode;
bool origin_mode;
/* not implemented: state of selective erase */
SavedCursor();
};
class DrawState {
private:
int width, height;
void new_grapheme( void );
void snap_cursor_to_border( void );
int cursor_col, cursor_row;
int combining_char_col, combining_char_row;
bool default_tabs;
std::vector<bool> tabs;
void reinitialize_tabs( unsigned int start );
int scrolling_region_top_row, scrolling_region_bottom_row;
Renditions renditions;
SavedCursor save;
public:
bool next_print_will_wrap;
bool origin_mode;
bool auto_wrap_mode;
bool insert_mode;
bool cursor_visible;
bool reverse_video;
bool bracketed_paste;
enum MouseReportingMode {
MOUSE_REPORTING_NONE = 0,
MOUSE_REPORTING_X10 = 9,
MOUSE_REPORTING_VT220 = 1000,
MOUSE_REPORTING_VT220_HILIGHT = 1001,
MOUSE_REPORTING_BTN_EVENT = 1002,
MOUSE_REPORTING_ANY_EVENT = 1003
} mouse_reporting_mode;
bool mouse_focus_event; // 1004
bool mouse_alternate_scroll; // 1007
enum MouseEncodingMode {
MOUSE_ENCODING_DEFAULT = 0,
MOUSE_ENCODING_UTF8 = 1005,
MOUSE_ENCODING_SGR = 1006,
MOUSE_ENCODING_URXVT = 1015
} mouse_encoding_mode;
bool application_mode_cursor_keys;
/* bold, etc. */
void move_row( int N, bool relative = false );
void move_col( int N, bool relative = false, bool implicit = false );
int get_cursor_col( void ) const { return cursor_col; }
int get_cursor_row( void ) const { return cursor_row; }
int get_combining_char_col( void ) const { return combining_char_col; }
int get_combining_char_row( void ) const { return combining_char_row; }
int get_width( void ) const { return width; }
int get_height( void ) const { return height; }
void set_tab( void );
void clear_tab( int col );
void clear_default_tabs( void ) { default_tabs = false; }
/* Default tabs can't be restored without resetting the draw state. */
int get_next_tab( int count ) const;
void set_scrolling_region( int top, int bottom );
int get_scrolling_region_top_row( void ) const { return scrolling_region_top_row; }
int get_scrolling_region_bottom_row( void ) const { return scrolling_region_bottom_row; }
int limit_top( void ) const;
int limit_bottom( void ) const;
void set_foreground_color( int x ) { renditions.set_foreground_color( x ); }
void set_background_color( int x ) { renditions.set_background_color( x ); }
void add_rendition( color_type x ) { renditions.set_rendition( x ); }
const Renditions& get_renditions( void ) const { return renditions; }
Renditions& get_renditions( void ) { return renditions; }
int get_background_rendition( void ) const { return renditions.get_background_rendition(); }
void save_cursor( void );
void restore_cursor( void );
void clear_saved_cursor( void ) { save = SavedCursor(); }
void resize( int s_width, int s_height );
DrawState( int s_width, int s_height );
bool operator==( const DrawState &x ) const
{
/* only compare fields that affect display */
return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
( bracketed_paste == x.bracketed_paste ) && ( mouse_reporting_mode == x.mouse_reporting_mode ) &&
( mouse_focus_event == x.mouse_focus_event ) && ( mouse_alternate_scroll == x.mouse_alternate_scroll) &&
( mouse_encoding_mode == x.mouse_encoding_mode );
}
};
class Framebuffer {
// To minimize copying of rows and cells, we use shared_ptr to
// share unchanged rows between multiple Framebuffers. If we
// write to a row in a Framebuffer and it is shared with other
// owners, we copy it first. The shared_ptr naturally manages the
// usage of the actual rows themselves.
//
// We gain a couple of free extras by doing this:
//
// * A quick check for equality between rows in different
// Framebuffers is to simply compare the pointer values. If they
// are equal, then the rows are obviously identical.
// * If no row is shared, the frame has not been modified.
public:
typedef std::vector<wchar_t> title_type;
typedef std::shared_ptr<Row> row_pointer;
typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */
private:
rows_type rows;
title_type icon_name;
title_type window_title;
title_type clipboard;
unsigned int bell_count;
bool title_initialized; /* true if the window title has been set via an OSC */
row_pointer newrow( void )
{
const size_t w = ds.get_width();
const color_type c = ds.get_background_rendition();
return std::make_shared<Row>( w, c );
}
public:
Framebuffer( int s_width, int s_height );
Framebuffer( const Framebuffer &other );
Framebuffer &operator=( const Framebuffer &other );
DrawState ds;
const rows_type &get_rows() const { return rows; }
void scroll( int N );
void move_rows_autoscroll( int rows );
inline const Row *get_row( int row ) const
{
if ( row == -1 ) row = ds.get_cursor_row();
return rows.at( row ).get();
}
inline const Cell *get_cell( int row = -1, int col = -1 ) const
{
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
return &rows.at( row )->cells.at( col );
}
Row *get_mutable_row( int row )
{
if ( row == -1 ) row = ds.get_cursor_row();
row_pointer &mutable_row = rows.at( row );
// If the row is shared, copy it.
if (!mutable_row.unique()) {
mutable_row = std::make_shared<Row>( *mutable_row );
}
return mutable_row.get();
}
Cell *get_mutable_cell( int row = -1, int col = -1 )
{
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
return &get_mutable_row( row )->cells.at( col );
}
Cell *get_combining_cell( void );
void apply_renditions_to_cell( Cell *cell );
void insert_line( int before_row, int count );
void delete_line( int row, int count );
void insert_cell( int row, int col );
void delete_cell( int row, int col );
void reset( void );
void soft_reset( void );
void set_title_initialized( void ) { title_initialized = true; }
bool is_title_initialized( void ) const { return title_initialized; }
void set_icon_name( const title_type &s ) { icon_name = s; }
void set_window_title( const title_type &s ) { window_title = s; }
void set_clipboard( const title_type &s ) { clipboard = s; }
const title_type & get_icon_name( void ) const { return icon_name; }
const title_type & get_window_title( void ) const { return window_title; }
const title_type & get_clipboard( void ) const { return clipboard; }
void prefix_window_title( const title_type &s );
void resize( int s_width, int s_height );
void reset_cell( Cell *c ) { c->reset( ds.get_background_rendition() ); }
void reset_row( Row *r ) { r->reset( ds.get_background_rendition() ); }
void ring_bell( void ) { bell_count++; }
unsigned int get_bell_count( void ) const { return bell_count; }
bool operator==( const Framebuffer &x ) const
{
return ( rows == x.rows ) && ( window_title == x.window_title ) && ( clipboard == x.clipboard ) && ( bell_count == x.bell_count ) && ( ds == x.ds );
}
};
} }
#endif #endif
+186 -185
View File
@@ -36,15 +36,15 @@
#include <unistd.h> #include <unistd.h>
#include "terminaldispatcher.h"
#include "src/terminal/terminalframebuffer.h"
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include "src/terminal/terminalframebuffer.h"
#include "terminaldispatcher.h"
using namespace Terminal; using namespace Terminal;
/* Terminal functions -- routines activated by CSI, escape or a control char */ /* Terminal functions -- routines activated by CSI, escape or a control char */
static void clearline( Framebuffer *fb, int row, int start, int end ) static void clearline( Framebuffer* fb, int row, int start, int end )
{ {
for ( int col = start; col <= end; col++ ) { for ( int col = start; col <= end; col++ ) {
fb->reset_cell( fb->get_mutable_cell( row, col ) ); fb->reset_cell( fb->get_mutable_cell( row, col ) );
@@ -52,77 +52,78 @@ static void clearline( Framebuffer *fb, int row, int start, int end )
} }
/* erase in line */ /* erase in line */
static void CSI_EL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_EL( Framebuffer* fb, Dispatcher* dispatch )
{ {
switch ( dispatch->getparam( 0, 0 ) ) { switch ( dispatch->getparam( 0, 0 ) ) {
case 0: /* default: active position to end of line, inclusive */ case 0: /* default: active position to end of line, inclusive */
clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 );
break; break;
case 1: /* start of screen to active position, inclusive */ case 1: /* start of screen to active position, inclusive */
clearline( fb, -1, 0, fb->ds.get_cursor_col() ); clearline( fb, -1, 0, fb->ds.get_cursor_col() );
break; break;
case 2: /* all of line */ case 2: /* all of line */
fb->reset_row( fb->get_mutable_row( -1 ) ); fb->reset_row( fb->get_mutable_row( -1 ) );
break; break;
default: default:
break; break;
} }
} }
static Function func_CSI_EL( CSI, "K", CSI_EL ); static Function func_CSI_EL( CSI, "K", CSI_EL );
/* erase in display */ /* erase in display */
static void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) { static void CSI_ED( Framebuffer* fb, Dispatcher* dispatch )
{
switch ( dispatch->getparam( 0, 0 ) ) { switch ( dispatch->getparam( 0, 0 ) ) {
case 0: /* active position to end of screen, inclusive */ case 0: /* active position to end of screen, inclusive */
clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 );
for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) { for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) {
fb->reset_row( fb->get_mutable_row( y ) ); fb->reset_row( fb->get_mutable_row( y ) );
} }
break; break;
case 1: /* start of screen to active position, inclusive */ case 1: /* start of screen to active position, inclusive */
for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) { for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) {
fb->reset_row( fb->get_mutable_row( y ) ); fb->reset_row( fb->get_mutable_row( y ) );
} }
clearline( fb, -1, 0, fb->ds.get_cursor_col() ); clearline( fb, -1, 0, fb->ds.get_cursor_col() );
break; break;
case 2: /* entire screen */ case 2: /* entire screen */
for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) {
fb->reset_row( fb->get_mutable_row( y ) ); fb->reset_row( fb->get_mutable_row( y ) );
} }
break; break;
default: default:
break; break;
} }
} }
static Function func_CSI_ED( CSI, "J", CSI_ED ); static Function func_CSI_ED( CSI, "J", CSI_ED );
/* cursor movement -- relative and absolute */ /* cursor movement -- relative and absolute */
static void CSI_cursormove( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_cursormove( Framebuffer* fb, Dispatcher* dispatch )
{ {
int num = dispatch->getparam( 0, 1 ); int num = dispatch->getparam( 0, 1 );
switch ( dispatch->get_dispatch_chars()[ 0 ] ) { switch ( dispatch->get_dispatch_chars()[0] ) {
case 'A': case 'A':
fb->ds.move_row( -num, true ); fb->ds.move_row( -num, true );
break; break;
case 'B': case 'B':
fb->ds.move_row( num, true ); fb->ds.move_row( num, true );
break; break;
case 'C': case 'C':
fb->ds.move_col( num, true ); fb->ds.move_col( num, true );
break; break;
case 'D': case 'D':
fb->ds.move_col( -num, true ); fb->ds.move_col( -num, true );
break; break;
case 'H': case 'H':
case 'f': case 'f':
fb->ds.move_row( dispatch->getparam( 0, 1 ) - 1 ); fb->ds.move_row( dispatch->getparam( 0, 1 ) - 1 );
fb->ds.move_col( dispatch->getparam( 1, 1 ) - 1 ); fb->ds.move_col( dispatch->getparam( 1, 1 ) - 1 );
break; break;
default: default:
break; break;
} }
} }
@@ -134,7 +135,7 @@ static Function func_CSI_cursormove_H( CSI, "H", CSI_cursormove );
static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove ); static Function func_CSI_cursormove_f( CSI, "f", CSI_cursormove );
/* device attributes */ /* device attributes */
static void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) static void CSI_DA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch )
{ {
dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */ dispatch->terminal_to_host.append( "\033[?62c" ); /* plain vt220 */
} }
@@ -142,7 +143,7 @@ static void CSI_DA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch
static Function func_CSI_DA( CSI, "c", CSI_DA ); static Function func_CSI_DA( CSI, "c", CSI_DA );
/* secondary device attributes */ /* secondary device attributes */
static void CSI_SDA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch ) static void CSI_SDA( Framebuffer* fb __attribute( ( unused ) ), Dispatcher* dispatch )
{ {
dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */ dispatch->terminal_to_host.append( "\033[>1;10;0c" ); /* plain vt220 */
} }
@@ -150,7 +151,7 @@ static void CSI_SDA( Framebuffer *fb __attribute((unused)), Dispatcher *dispatch
static Function func_CSI_SDA( CSI, ">c", CSI_SDA ); static Function func_CSI_SDA( CSI, ">c", CSI_SDA );
/* screen alignment diagnostic */ /* screen alignment diagnostic */
static void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECALN( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) {
for ( int x = 0; x < fb->ds.get_width(); x++ ) { for ( int x = 0; x < fb->ds.get_width(); x++ ) {
@@ -163,7 +164,7 @@ static void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unuse
static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN ); static Function func_Esc_DECALN( ESCAPE, "#8", Esc_DECALN );
/* line feed */ /* line feed */
static void Ctrl_LF( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_LF( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->move_rows_autoscroll( 1 ); fb->move_rows_autoscroll( 1 );
} }
@@ -175,7 +176,7 @@ static Function func_Ctrl_VT( CONTROL, "\x0b", Ctrl_LF );
static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF ); static Function func_Ctrl_FF( CONTROL, "\x0c", Ctrl_LF );
/* carriage return */ /* carriage return */
static void Ctrl_CR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_CR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
} }
@@ -183,7 +184,7 @@ static void Ctrl_CR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR ); static Function func_Ctrl_CR( CONTROL, "\x0d", Ctrl_CR );
/* backspace */ /* backspace */
static void Ctrl_BS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_BS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( -1, true ); fb->ds.move_col( -1, true );
} }
@@ -191,7 +192,7 @@ static void Ctrl_BS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS ); static Function func_Ctrl_BS( CONTROL, "\x08", Ctrl_BS );
/* reverse index -- like a backwards line feed */ /* reverse index -- like a backwards line feed */
static void Ctrl_RI( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_RI( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->move_rows_autoscroll( -1 ); fb->move_rows_autoscroll( -1 );
} }
@@ -199,7 +200,7 @@ static void Ctrl_RI( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI ); static Function func_Ctrl_RI( CONTROL, "\x8D", Ctrl_RI );
/* newline */ /* newline */
static void Ctrl_NEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_NEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
fb->move_rows_autoscroll( 1 ); fb->move_rows_autoscroll( 1 );
@@ -208,7 +209,7 @@ static void Ctrl_NEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)
static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL ); static Function func_Ctrl_NEL( CONTROL, "\x85", Ctrl_NEL );
/* horizontal tab */ /* horizontal tab */
static void HT_n( Framebuffer *fb, size_t count ) static void HT_n( Framebuffer* fb, size_t count )
{ {
int col = fb->ds.get_next_tab( count ); int col = fb->ds.get_next_tab( count );
if ( col == -1 ) { /* no tabs, go to end of line */ if ( col == -1 ) { /* no tabs, go to end of line */
@@ -223,16 +224,16 @@ static void HT_n( Framebuffer *fb, size_t count )
fb->ds.next_print_will_wrap = wrap_state_save; fb->ds.next_print_will_wrap = wrap_state_save;
} }
static void Ctrl_HT( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_HT( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
HT_n( fb, 1 ); HT_n( fb, 1 );
} }
static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false ); static Function func_Ctrl_HT( CONTROL, "\x09", Ctrl_HT, false );
static void CSI_CxT( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_CxT( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 1 ); int param = dispatch->getparam( 0, 1 );
if ( dispatch->get_dispatch_chars()[ 0 ] == 'Z' ) { if ( dispatch->get_dispatch_chars()[0] == 'Z' ) {
param = -param; param = -param;
} }
if ( param == 0 ) { if ( param == 0 ) {
@@ -245,7 +246,7 @@ static Function func_CSI_CHT( CSI, "I", CSI_CxT, false );
static Function func_CSI_CBT( CSI, "Z", CSI_CxT, false ); static Function func_CSI_CBT( CSI, "Z", CSI_CxT, false );
/* horizontal tab set */ /* horizontal tab set */
static void Ctrl_HTS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Ctrl_HTS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.set_tab(); fb->ds.set_tab();
} }
@@ -253,76 +254,79 @@ static void Ctrl_HTS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)
static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS ); static Function func_Ctrl_HTS( CONTROL, "\x88", Ctrl_HTS );
/* tabulation clear */ /* tabulation clear */
static void CSI_TBC( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_TBC( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 0 ); int param = dispatch->getparam( 0, 0 );
switch ( param ) { switch ( param ) {
case 0: /* clear this tab stop */ case 0: /* clear this tab stop */
fb->ds.clear_tab( fb->ds.get_cursor_col() ); fb->ds.clear_tab( fb->ds.get_cursor_col() );
break; break;
case 3: /* clear all tab stops */ case 3: /* clear all tab stops */
fb->ds.clear_default_tabs(); fb->ds.clear_default_tabs();
for ( int x = 0; x < fb->ds.get_width(); x++ ) { for ( int x = 0; x < fb->ds.get_width(); x++ ) {
fb->ds.clear_tab( x ); fb->ds.clear_tab( x );
} }
break; break;
default: default:
break; break;
} }
} }
/* TBC preserves wrap state */ /* TBC preserves wrap state */
static Function func_CSI_TBC( CSI, "g", CSI_TBC, false ); static Function func_CSI_TBC( CSI, "g", CSI_TBC, false );
static bool *get_DEC_mode( int param, Framebuffer *fb ) { static bool* get_DEC_mode( int param, Framebuffer* fb )
{
switch ( param ) { switch ( param ) {
case 1: /* cursor key mode */ case 1: /* cursor key mode */
return &(fb->ds.application_mode_cursor_keys); return &( fb->ds.application_mode_cursor_keys );
case 3: /* 80/132. Ignore but clear screen. */ case 3: /* 80/132. Ignore but clear screen. */
/* clear screen */ /* clear screen */
fb->ds.move_row( 0 ); fb->ds.move_row( 0 );
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) {
fb->reset_row( fb->get_mutable_row( y ) ); fb->reset_row( fb->get_mutable_row( y ) );
} }
return NULL; return NULL;
case 5: /* reverse video */ case 5: /* reverse video */
return &(fb->ds.reverse_video); return &( fb->ds.reverse_video );
case 6: /* origin */ case 6: /* origin */
fb->ds.move_row( 0 ); fb->ds.move_row( 0 );
fb->ds.move_col( 0 ); fb->ds.move_col( 0 );
return &(fb->ds.origin_mode); return &( fb->ds.origin_mode );
case 7: /* auto wrap */ case 7: /* auto wrap */
return &(fb->ds.auto_wrap_mode); return &( fb->ds.auto_wrap_mode );
case 25: case 25:
return &(fb->ds.cursor_visible); return &( fb->ds.cursor_visible );
case 1004: /* xterm mouse focus event */ case 1004: /* xterm mouse focus event */
return &(fb->ds.mouse_focus_event); return &( fb->ds.mouse_focus_event );
case 1007: /* xterm mouse alternate scroll */ case 1007: /* xterm mouse alternate scroll */
return &(fb->ds.mouse_alternate_scroll); return &( fb->ds.mouse_alternate_scroll );
case 2004: /* bracketed paste */ case 2004: /* bracketed paste */
return &(fb->ds.bracketed_paste); return &( fb->ds.bracketed_paste );
default: default:
break; break;
} }
return NULL; return NULL;
} }
/* helper for CSI_DECSM and CSI_DECRM */ /* helper for CSI_DECSM and CSI_DECRM */
static void set_if_available( bool *mode, bool value ) static void set_if_available( bool* mode, bool value )
{ {
if ( mode ) { *mode = value; } if ( mode ) {
*mode = value;
}
} }
/* set private mode */ /* set private mode */
static void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECSM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int param = dispatch->getparam( i, 0 ); int param = dispatch->getparam( i, 0 );
if (param == 9 || (param >= 1000 && param <= 1003)) { if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) {
fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode) param; fb->ds.mouse_reporting_mode = (Terminal::DrawState::MouseReportingMode)param;
} else if (param == 1005 || param == 1006 || param == 1015) { } else if ( param == 1005 || param == 1006 || param == 1015 ) {
fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode) param; fb->ds.mouse_encoding_mode = (Terminal::DrawState::MouseEncodingMode)param;
} else { } else {
set_if_available( get_DEC_mode( param, fb ), true ); set_if_available( get_DEC_mode( param, fb ), true );
} }
@@ -330,13 +334,13 @@ static void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
} }
/* clear private mode */ /* clear private mode */
static void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECRM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int param = dispatch->getparam( i, 0 ); int param = dispatch->getparam( i, 0 );
if (param == 9 || (param >= 1000 && param <= 1003)) { if ( param == 9 || ( param >= 1000 && param <= 1003 ) ) {
fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE; fb->ds.mouse_reporting_mode = Terminal::DrawState::MOUSE_REPORTING_NONE;
} else if (param == 1005 || param == 1006 || param == 1015) { } else if ( param == 1005 || param == 1006 || param == 1015 ) {
fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT; fb->ds.mouse_encoding_mode = Terminal::DrawState::MOUSE_ENCODING_DEFAULT;
} else { } else {
set_if_available( get_DEC_mode( param, fb ), false ); set_if_available( get_DEC_mode( param, fb ), false );
@@ -348,18 +352,19 @@ static void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false ); static Function func_CSI_DECSM( CSI, "?h", CSI_DECSM, false );
static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false ); static Function func_CSI_DECRM( CSI, "?l", CSI_DECRM, false );
static bool *get_ANSI_mode( int param, Framebuffer *fb ) { static bool* get_ANSI_mode( int param, Framebuffer* fb )
{
if ( param == 4 ) { /* insert/replace mode */ if ( param == 4 ) { /* insert/replace mode */
return &(fb->ds.insert_mode); return &( fb->ds.insert_mode );
} }
return NULL; return NULL;
} }
/* set mode */ /* set mode */
static void CSI_SM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb );
if ( mode ) { if ( mode ) {
*mode = true; *mode = true;
} }
@@ -367,10 +372,10 @@ static void CSI_SM( Framebuffer *fb, Dispatcher *dispatch )
} }
/* clear mode */ /* clear mode */
static void CSI_RM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_RM( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
bool *mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb ); bool* mode = get_ANSI_mode( dispatch->getparam( i, 0 ), fb );
if ( mode ) { if ( mode ) {
*mode = false; *mode = false;
} }
@@ -381,14 +386,12 @@ static Function func_CSI_SM( CSI, "h", CSI_SM );
static Function func_CSI_RM( CSI, "l", CSI_RM ); static Function func_CSI_RM( CSI, "l", CSI_RM );
/* set top and bottom margins */ /* set top and bottom margins */
static void CSI_DECSTBM( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DECSTBM( Framebuffer* fb, Dispatcher* dispatch )
{ {
int top = dispatch->getparam( 0, 1 ); int top = dispatch->getparam( 0, 1 );
int bottom = dispatch->getparam( 1, fb->ds.get_height() ); int bottom = dispatch->getparam( 1, fb->ds.get_height() );
if ( (bottom <= top) if ( ( bottom <= top ) || ( top > fb->ds.get_height() ) || ( top == 0 && bottom == 1 ) ) {
|| (top > fb->ds.get_height())
|| (top == 0 && bottom == 1) ) {
return; /* invalid, xterm ignores */ return; /* invalid, xterm ignores */
} }
@@ -400,14 +403,15 @@ static void CSI_DECSTBM( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM ); static Function func_CSI_DECSTMB( CSI, "r", CSI_DECSTBM );
/* terminal bell */ /* terminal bell */
static void Ctrl_BEL( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { static void Ctrl_BEL( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{
fb->ring_bell(); fb->ring_bell();
} }
static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL ); static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL );
/* select graphics rendition -- e.g., bold, blinking, etc. */ /* select graphics rendition -- e.g., bold, blinking, etc. */
static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SGR( Framebuffer* fb, Dispatcher* dispatch )
{ {
for ( int i = 0; i < dispatch->param_count(); i++ ) { for ( int i = 0; i < dispatch->param_count(); i++ ) {
int rendition = dispatch->getparam( i, 0 ); int rendition = dispatch->getparam( i, 0 );
@@ -415,23 +419,20 @@ static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch )
because Ps of 0 in that case does not mean reset to default, even because Ps of 0 in that case does not mean reset to default, even
though it means that otherwise (as usually renditions are applied though it means that otherwise (as usually renditions are applied
in order). */ in order). */
if ((rendition == 38 || rendition == 48) && if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 3 )
(dispatch->param_count() - i >= 3) && && ( dispatch->getparam( i + 1, -1 ) == 5 ) ) {
(dispatch->getparam( i+1, -1 ) == 5)) { ( rendition == 38 ) ? fb->ds.set_foreground_color( dispatch->getparam( i + 2, 0 ) )
(rendition == 38) ? : fb->ds.set_background_color( dispatch->getparam( i + 2, 0 ) );
fb->ds.set_foreground_color( dispatch->getparam( i+2, 0 ) ) :
fb->ds.set_background_color( dispatch->getparam( i+2, 0 ) );
i += 2; i += 2;
continue; continue;
} }
/* True color support: ESC[ ... [34]8;2;<r>;<g>;<b> ... m */ /* True color support: ESC[ ... [34]8;2;<r>;<g>;<b> ... m */
if ( (rendition == 38 || rendition == 48) && if ( ( rendition == 38 || rendition == 48 ) && ( dispatch->param_count() - i >= 5 )
(dispatch->param_count() - i >= 5) && && ( dispatch->getparam( i + 1, -1 ) == 2 ) ) {
(dispatch->getparam( i+1, -1 ) == 2)) { unsigned int red = dispatch->getparam( i + 2, 0 );
unsigned int red = dispatch->getparam(i+2, 0); unsigned int green = dispatch->getparam( i + 3, 0 );
unsigned int green = dispatch->getparam(i+3, 0); unsigned int blue = dispatch->getparam( i + 4, 0 );
unsigned int blue = dispatch->getparam(i+4, 0);
unsigned int color; unsigned int color;
color = Renditions::make_true_color( red, green, blue ); color = Renditions::make_true_color( red, green, blue );
@@ -452,12 +453,12 @@ static void CSI_SGR( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */ static Function func_CSI_SGR( CSI, "m", CSI_SGR, false ); /* changing renditions doesn't clear wrap flag */
/* save and restore cursor */ /* save and restore cursor */
static void Esc_DECSC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECSC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.save_cursor(); fb->ds.save_cursor();
} }
static void Esc_DECRC( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_DECRC( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->ds.restore_cursor(); fb->ds.restore_cursor();
} }
@@ -466,30 +467,28 @@ static Function func_Esc_DECSC( ESCAPE, "7", Esc_DECSC );
static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC ); static Function func_Esc_DECRC( ESCAPE, "8", Esc_DECRC );
/* device status report -- e.g., cursor position (used by resize) */ /* device status report -- e.g., cursor position (used by resize) */
static void CSI_DSR( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DSR( Framebuffer* fb, Dispatcher* dispatch )
{ {
int param = dispatch->getparam( 0, 0 ); int param = dispatch->getparam( 0, 0 );
switch ( param ) { switch ( param ) {
case 5: /* device status report requested */ case 5: /* device status report requested */
dispatch->terminal_to_host.append( "\033[0n" ); dispatch->terminal_to_host.append( "\033[0n" );
break; break;
case 6: /* report of active position requested */ case 6: /* report of active position requested */
char cpr[ 32 ]; char cpr[32];
snprintf( cpr, 32, "\033[%d;%dR", snprintf( cpr, 32, "\033[%d;%dR", fb->ds.get_cursor_row() + 1, fb->ds.get_cursor_col() + 1 );
fb->ds.get_cursor_row() + 1, dispatch->terminal_to_host.append( cpr );
fb->ds.get_cursor_col() + 1 ); break;
dispatch->terminal_to_host.append( cpr ); default:
break; break;
default:
break;
} }
} }
static Function func_CSI_DSR( CSI, "n", CSI_DSR ); static Function func_CSI_DSR( CSI, "n", CSI_DSR );
/* insert line */ /* insert line */
static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_IL( Framebuffer* fb, Dispatcher* dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
@@ -503,7 +502,7 @@ static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_IL( CSI, "L", CSI_IL ); static Function func_CSI_IL( CSI, "L", CSI_IL );
/* delete line */ /* delete line */
static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DL( Framebuffer* fb, Dispatcher* dispatch )
{ {
int lines = dispatch->getparam( 0, 1 ); int lines = dispatch->getparam( 0, 1 );
@@ -517,7 +516,7 @@ static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DL( CSI, "M", CSI_DL ); static Function func_CSI_DL( CSI, "M", CSI_DL );
/* insert characters */ /* insert characters */
static void CSI_ICH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_ICH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int cells = dispatch->getparam( 0, 1 ); int cells = dispatch->getparam( 0, 1 );
@@ -529,7 +528,7 @@ static void CSI_ICH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_ICH( CSI, "@", CSI_ICH ); static Function func_CSI_ICH( CSI, "@", CSI_ICH );
/* delete character */ /* delete character */
static void CSI_DCH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_DCH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int cells = dispatch->getparam( 0, 1 ); int cells = dispatch->getparam( 0, 1 );
@@ -541,7 +540,7 @@ static void CSI_DCH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_DCH( CSI, "P", CSI_DCH ); static Function func_CSI_DCH( CSI, "P", CSI_DCH );
/* line position absolute */ /* line position absolute */
static void CSI_VPA( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_VPA( Framebuffer* fb, Dispatcher* dispatch )
{ {
int row = dispatch->getparam( 0, 1 ); int row = dispatch->getparam( 0, 1 );
fb->ds.move_row( row - 1 ); fb->ds.move_row( row - 1 );
@@ -550,17 +549,17 @@ static void CSI_VPA( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_VPA( CSI, "d", CSI_VPA ); static Function func_CSI_VPA( CSI, "d", CSI_VPA );
/* character position absolute */ /* character position absolute */
static void CSI_HPA( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_HPA( Framebuffer* fb, Dispatcher* dispatch )
{ {
int col = dispatch->getparam( 0, 1 ); int col = dispatch->getparam( 0, 1 );
fb->ds.move_col( col - 1 ); fb->ds.move_col( col - 1 );
} }
static Function func_CSI_CHA( CSI, "G", CSI_HPA ); /* ECMA-48 name: CHA */ static Function func_CSI_CHA( CSI, "G", CSI_HPA ); /* ECMA-48 name: CHA */
static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */ static Function func_CSI_HPA( CSI, "\x60", CSI_HPA ); /* ECMA-48 name: HPA */
/* erase character */ /* erase character */
static void CSI_ECH( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_ECH( Framebuffer* fb, Dispatcher* dispatch )
{ {
int num = dispatch->getparam( 0, 1 ); int num = dispatch->getparam( 0, 1 );
int limit = fb->ds.get_cursor_col() + num - 1; int limit = fb->ds.get_cursor_col() + num - 1;
@@ -574,7 +573,7 @@ static void CSI_ECH( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_ECH( CSI, "X", CSI_ECH ); static Function func_CSI_ECH( CSI, "X", CSI_ECH );
/* reset to initial state */ /* reset to initial state */
static void Esc_RIS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void Esc_RIS( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->reset(); fb->reset();
} }
@@ -582,7 +581,7 @@ static void Esc_RIS( Framebuffer *fb, Dispatcher *dispatch __attribute((unused))
static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS ); static Function func_Esc_RIS( ESCAPE, "c", Esc_RIS );
/* soft reset */ /* soft reset */
static void CSI_DECSTR( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) static void CSI_DECSTR( Framebuffer* fb, Dispatcher* dispatch __attribute( ( unused ) ) )
{ {
fb->soft_reset(); fb->soft_reset();
} }
@@ -590,45 +589,47 @@ static void CSI_DECSTR( Framebuffer *fb, Dispatcher *dispatch __attribute((unuse
static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR ); static Function func_CSI_DECSTR( CSI, "!p", CSI_DECSTR );
/* xterm uses an Operating System Command to set the window title */ /* xterm uses an Operating System Command to set the window title */
void Dispatcher::OSC_dispatch( const Parser::OSC_End *act __attribute((unused)), Framebuffer *fb ) void Dispatcher::OSC_dispatch( const Parser::OSC_End* act __attribute( ( unused ) ), Framebuffer* fb )
{ {
/* handle osc copy clipboard sequence 52;c; */ /* handle osc copy clipboard sequence 52;c; */
if ( OSC_string.size() >= 5 && OSC_string[ 0 ] == L'5' && if ( OSC_string.size() >= 5 && OSC_string[0] == L'5' && OSC_string[1] == L'2' && OSC_string[2] == L';'
OSC_string[ 1 ] == L'2' && OSC_string[ 2 ] == L';' && && OSC_string[3] == L'c' && OSC_string[4] == L';' ) {
OSC_string[ 3 ] == L'c' && OSC_string[ 4 ] == L';') { Terminal::Framebuffer::title_type clipboard( OSC_string.begin() + 5, OSC_string.end() );
Terminal::Framebuffer::title_type clipboard( fb->set_clipboard( clipboard );
OSC_string.begin() + 5, OSC_string.end() ); /* handle osc terminal title sequence */
fb->set_clipboard( clipboard );
/* handle osc terminal title sequence */
} else if ( OSC_string.size() >= 1 ) { } else if ( OSC_string.size() >= 1 ) {
long cmd_num = -1; long cmd_num = -1;
int offset = 0; int offset = 0;
if ( OSC_string[ 0 ] == L';' ) { if ( OSC_string[0] == L';' ) {
/* OSC of the form "\033];<title>\007" */ /* OSC of the form "\033];<title>\007" */
cmd_num = 0; /* treat it as as a zero */ cmd_num = 0; /* treat it as as a zero */
offset = 1; offset = 1;
} else if ( (OSC_string.size() >= 2) && (OSC_string[ 1 ] == L';') ) { } else if ( ( OSC_string.size() >= 2 ) && ( OSC_string[1] == L';' ) ) {
/* OSC of the form "\033]X;<title>\007" where X can be: /* OSC of the form "\033]X;<title>\007" where X can be:
* 0: set icon name and window title * 0: set icon name and window title
* 1: set icon name * 1: set icon name
* 2: set window title */ * 2: set window title */
cmd_num = OSC_string[ 0 ] - L'0'; cmd_num = OSC_string[0] - L'0';
offset = 2; offset = 2;
} }
bool set_icon = cmd_num == 0 || cmd_num == 1; bool set_icon = cmd_num == 0 || cmd_num == 1;
bool set_title = cmd_num == 0 || cmd_num == 2; bool set_title = cmd_num == 0 || cmd_num == 2;
if ( set_icon || set_title ) { if ( set_icon || set_title ) {
fb->set_title_initialized(); fb->set_title_initialized();
int title_length = std::min(OSC_string.size(), (size_t)256); int title_length = std::min( OSC_string.size(), (size_t)256 );
Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.begin() + title_length ); Terminal::Framebuffer::title_type newtitle( OSC_string.begin() + offset, OSC_string.begin() + title_length );
if ( set_icon ) { fb->set_icon_name( newtitle ); } if ( set_icon ) {
if ( set_title ) { fb->set_window_title( newtitle ); } fb->set_icon_name( newtitle );
}
if ( set_title ) {
fb->set_window_title( newtitle );
}
} }
} }
} }
/* scroll down or terminfo indn */ /* scroll down or terminfo indn */
static void CSI_SD( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SD( Framebuffer* fb, Dispatcher* dispatch )
{ {
fb->scroll( dispatch->getparam( 0, 1 ) ); fb->scroll( dispatch->getparam( 0, 1 ) );
} }
@@ -636,7 +637,7 @@ static void CSI_SD( Framebuffer *fb, Dispatcher *dispatch )
static Function func_CSI_SD( CSI, "S", CSI_SD ); static Function func_CSI_SD( CSI, "S", CSI_SD );
/* scroll up or terminfo rin */ /* scroll up or terminfo rin */
static void CSI_SU( Framebuffer *fb, Dispatcher *dispatch ) static void CSI_SU( Framebuffer* fb, Dispatcher* dispatch )
{ {
fb->scroll( -dispatch->getparam( 0, 1 ) ); fb->scroll( -dispatch->getparam( 0, 1 ) );
} }
+27 -30
View File
@@ -36,8 +36,7 @@
using namespace Terminal; using namespace Terminal;
std::string UserInput::input( const Parser::UserByte *act, std::string UserInput::input( const Parser::UserByte* act, bool application_mode_cursor_keys )
bool application_mode_cursor_keys )
{ {
/* The user will always be in application mode. If stm is not in /* The user will always be in application mode. If stm is not in
application mode, convert user's cursor control function to an application mode, convert user's cursor control function to an
@@ -50,35 +49,33 @@ std::string UserInput::input( const Parser::UserByte *act,
two octets in UTF-8. Fortunately nobody seems to send this. */ two octets in UTF-8. Fortunately nobody seems to send this. */
switch ( state ) { switch ( state ) {
case Ground: case Ground:
if ( act->c == 0x1b ) { /* ESC */ if ( act->c == 0x1b ) { /* ESC */
state = ESC; state = ESC;
} }
return std::string( &act->c, 1 ); return std::string( &act->c, 1 );
case ESC: case ESC:
if ( act->c == 'O' ) { /* ESC O = 7-bit SS3 */ if ( act->c == 'O' ) { /* ESC O = 7-bit SS3 */
state = SS3; state = SS3;
return std::string();
}
state = Ground;
return std::string( &act->c, 1 );
case SS3:
state = Ground;
if ( ( !application_mode_cursor_keys ) && ( act->c >= 'A' ) && ( act->c <= 'D' ) ) {
char translated_cursor[2] = { '[', act->c };
return std::string( translated_cursor, 2 );
} else {
char original_cursor[2] = { 'O', act->c };
return std::string( original_cursor, 2 );
}
default:
assert( !"unexpected state" );
state = Ground;
return std::string(); return std::string();
}
state = Ground;
return std::string( &act->c, 1 );
case SS3:
state = Ground;
if ( (!application_mode_cursor_keys)
&& (act->c >= 'A')
&& (act->c <= 'D') ) {
char translated_cursor[ 2 ] = { '[', act->c };
return std::string( translated_cursor, 2 );
} else {
char original_cursor[ 2 ] = { 'O', act->c };
return std::string( original_cursor, 2 );
}
default:
assert( !"unexpected state" );
state = Ground;
return std::string();
} }
} }
+20 -21
View File
@@ -33,31 +33,30 @@
#ifndef TERMINALUSERINPUT_HPP #ifndef TERMINALUSERINPUT_HPP
#define TERMINALUSERINPUT_HPP #define TERMINALUSERINPUT_HPP
#include <string>
#include "src/terminal/parseraction.h" #include "src/terminal/parseraction.h"
#include <string>
namespace Terminal { namespace Terminal {
class UserInput { class UserInput
public: {
enum UserInputState { public:
Ground, enum UserInputState
ESC, {
SS3 Ground,
}; ESC,
SS3
private:
UserInputState state;
public:
UserInput()
: state( Ground )
{}
std::string input( const Parser::UserByte *act,
bool application_mode_cursor_keys );
bool operator==( const UserInput &x ) const { return state == x.state; }
}; };
private:
UserInputState state;
public:
UserInput() : state( Ground ) {}
std::string input( const Parser::UserByte* act, bool application_mode_cursor_keys );
bool operator==( const UserInput& x ) const { return state == x.state; }
};
} }
#endif #endif
+27 -26
View File
@@ -38,54 +38,55 @@
the Mosh project. */ the Mosh project. */
#include <cstdint> #include <cstdint>
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include "src/crypto/base64.h"
#include "base64_vector.h" #include "base64_vector.h"
#include "src/crypto/base64.h"
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
#include "src/crypto/prng.h" #include "src/crypto/prng.h"
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
// #include "test_utils.h" // #include "test_utils.h"
#define KEY_LEN 16 #define KEY_LEN 16
#define NONCE_LEN 12 #define NONCE_LEN 12
#define TAG_LEN 16 #define TAG_LEN 16
bool verbose = false; bool verbose = false;
static void test_base64( void ) { static void test_base64( void )
{
/* run through a test vector */ /* run through a test vector */
char encoded[25]; char encoded[25];
uint8_t decoded[16]; uint8_t decoded[16];
size_t b64_len = 24; size_t b64_len = 24;
size_t raw_len = 16; size_t raw_len = 16;
for ( base64_test_row *row = static_base64_vector; *row->native != '\0'; row++ ) { for ( base64_test_row* row = static_base64_vector; *row->native != '\0'; row++ ) {
memset(encoded, '\0', sizeof encoded); memset( encoded, '\0', sizeof encoded );
memset(decoded, '\0', sizeof decoded); memset( decoded, '\0', sizeof decoded );
base64_encode(static_cast<const uint8_t *>(row->native), raw_len, encoded, b64_len); base64_encode( static_cast<const uint8_t*>( row->native ), raw_len, encoded, b64_len );
fatal_assert( b64_len == 24 ); fatal_assert( b64_len == 24 );
fatal_assert( !memcmp(row->encoded, encoded, sizeof encoded)); fatal_assert( !memcmp( row->encoded, encoded, sizeof encoded ) );
fatal_assert( base64_decode(row->encoded, b64_len, decoded, &raw_len )); fatal_assert( base64_decode( row->encoded, b64_len, decoded, &raw_len ) );
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
fatal_assert( !memcmp(row->native, decoded, sizeof decoded)); fatal_assert( !memcmp( row->native, decoded, sizeof decoded ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "validation PASSED\n" ); printf( "validation PASSED\n" );
} }
/* try 0..255 in the last byte; make sure the final two characters are output properly */ /* try 0..255 in the last byte; make sure the final two characters are output properly */
uint8_t source[16]; uint8_t source[16];
memset(source, '\0', sizeof source); memset( source, '\0', sizeof source );
for ( int i = 0; i < 256; i++ ) { for ( int i = 0; i < 256; i++ ) {
source[15] = i; source[15] = i;
base64_encode(source, raw_len, encoded, b64_len); base64_encode( source, raw_len, encoded, b64_len );
fatal_assert( b64_len == 24 ); fatal_assert( b64_len == 24 );
fatal_assert( base64_decode(encoded, b64_len, decoded, &raw_len )); fatal_assert( base64_decode( encoded, b64_len, decoded, &raw_len ) );
fatal_assert( raw_len == 16 ); fatal_assert( raw_len == 16 );
fatal_assert( !memcmp(source, decoded, sizeof decoded)); fatal_assert( !memcmp( source, decoded, sizeof decoded ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "last-byte PASSED\n" ); printf( "last-byte PASSED\n" );
@@ -93,17 +94,17 @@ static void test_base64( void ) {
/* randomly try keys */ /* randomly try keys */
PRNG prng; PRNG prng;
for ( int i = 0; i < ( 1<<17 ); i++ ) { for ( int i = 0; i < ( 1 << 17 ); i++ ) {
Base64Key key1(prng); Base64Key key1( prng );
Base64Key key2(key1.printable_key()); Base64Key key2( key1.printable_key() );
fatal_assert( key1.printable_key() == key2.printable_key() && !memcmp(key1.data(), key2.data(), 16 )); fatal_assert( key1.printable_key() == key2.printable_key() && !memcmp( key1.data(), key2.data(), 16 ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "random PASSED\n" ); printf( "random PASSED\n" );
} }
/* test bad keys */ /* test bad keys */
const char *bad_keys[] = { const char* bad_keys[] = {
"", "",
"AAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAAA=", "AAAAAAAAAAAAAAAAAAAAAA=",
@@ -119,25 +120,25 @@ static void test_base64( void ) {
"AAAAAAAAAA==", "AAAAAAAAAA==",
NULL, NULL,
}; };
for ( const char **key = bad_keys; *key != NULL; key++ ) { for ( const char** key = bad_keys; *key != NULL; key++ ) {
b64_len = 24; b64_len = 24;
raw_len = 16; raw_len = 16;
fatal_assert( !base64_decode(*key, b64_len, decoded, &raw_len )); fatal_assert( !base64_decode( *key, b64_len, decoded, &raw_len ) );
} }
if ( verbose ) { if ( verbose ) {
printf( "bad-keys PASSED\n" ); printf( "bad-keys PASSED\n" );
} }
} }
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
try { try {
test_base64(); test_base64();
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
return 1; return 1;
} }
+6 -2
View File
@@ -1,3 +1,7 @@
struct base64_test_row { const unsigned char native[17]; const char encoded[25]; }; struct base64_test_row
typedef base64_test_row *base64_test_vector; {
const unsigned char native[17];
const char encoded[25];
};
typedef base64_test_row* base64_test_vector;
extern base64_test_row static_base64_vector[]; extern base64_test_row static_base64_vector[];
+19 -16
View File
@@ -48,35 +48,37 @@ using namespace Crypto;
PRNG prng; PRNG prng;
const size_t MESSAGE_SIZE_MAX = (2048 - 16); const size_t MESSAGE_SIZE_MAX = ( 2048 - 16 );
const size_t MESSAGES_PER_SESSION = 256; const size_t MESSAGES_PER_SESSION = 256;
const size_t NUM_SESSIONS = 64; const size_t NUM_SESSIONS = 64;
bool verbose = false; bool verbose = false;
#define NONCE_FMT "%016" PRIx64 #define NONCE_FMT "%016" PRIx64
static std::string random_payload( void ) { static std::string random_payload( void )
{
const size_t len = prng.uint32() % MESSAGE_SIZE_MAX; const size_t len = prng.uint32() % MESSAGE_SIZE_MAX;
char buf[ MESSAGE_SIZE_MAX ]; char buf[MESSAGE_SIZE_MAX];
prng.fill( buf, len ); prng.fill( buf, len );
std::string payload( buf, len ); std::string payload( buf, len );
return payload; return payload;
} }
static void test_bad_decrypt( Session &decryption_session ) { static void test_bad_decrypt( Session& decryption_session )
{
std::string bad_ct = random_payload(); std::string bad_ct = random_payload();
bool got_exn = false; bool got_exn = false;
try { try {
decryption_session.decrypt( bad_ct ); decryption_session.decrypt( bad_ct );
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
got_exn = true; got_exn = true;
/* The "bad decrypt" exception needs to be non-fatal, otherwise we are /* The "bad decrypt" exception needs to be non-fatal, otherwise we are
vulnerable to an easy DoS. */ vulnerable to an easy DoS. */
fatal_assert( ! e.fatal ); fatal_assert( !e.fatal );
} }
if ( verbose ) { if ( verbose ) {
@@ -86,7 +88,8 @@ static void test_bad_decrypt( Session &decryption_session ) {
} }
/* Generate a single key and initial nonce, then perform some encryptions. */ /* Generate a single key and initial nonce, then perform some encryptions. */
static void test_one_session( void ) { static void test_one_session( void )
{
Base64Key key; Base64Key key;
Session encryption_session( key ); Session encryption_session( key );
Session decryption_session( key ); Session decryption_session( key );
@@ -97,7 +100,7 @@ static void test_one_session( void ) {
hexdump( key.data(), 16, "key" ); hexdump( key.data(), 16, "key" );
} }
for ( size_t i=0; i<MESSAGES_PER_SESSION; i++ ) { for ( size_t i = 0; i < MESSAGES_PER_SESSION; i++ ) {
Nonce nonce( nonce_int ); Nonce nonce( nonce_int );
fatal_assert( nonce.val() == nonce_int ); fatal_assert( nonce.val() == nonce_int );
@@ -123,7 +126,7 @@ static void test_one_session( void ) {
nonce_int++; nonce_int++;
if ( ! ( prng.uint8() % 16 ) ) { if ( !( prng.uint8() % 16 ) ) {
test_bad_decrypt( decryption_session ); test_bad_decrypt( decryption_session );
} }
@@ -133,17 +136,17 @@ static void test_one_session( void ) {
} }
} }
int main( int argc, char *argv[] ) { int main( int argc, char* argv[] )
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { {
if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
for ( size_t i=0; i<NUM_SESSIONS; i++ ) { for ( size_t i = 0; i < NUM_SESSIONS; i++ ) {
try { try {
test_one_session(); test_one_session();
} catch ( const CryptoException &e ) { } catch ( const CryptoException& e ) {
fprintf( stderr, "Crypto exception: %s\r\n", fprintf( stderr, "Crypto exception: %s\r\n", e.what() );
e.what() );
return 1; return 1;
} }
} }
+4 -4
View File
@@ -56,9 +56,9 @@
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#include "src/util/swrite.h" #include "src/util/swrite.h"
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if (argc < 2) { if ( argc < 2 ) {
fprintf( stderr, "usage: inpty COMMAND [ARGS...]\n" ); fprintf( stderr, "usage: inpty COMMAND [ARGS...]\n" );
return 1; return 1;
} }
@@ -68,7 +68,7 @@ int main( int argc, char *argv[] )
winsize.ws_col = 80; winsize.ws_col = 80;
winsize.ws_row = 24; winsize.ws_row = 24;
int saved_stderr = dup(STDERR_FILENO); int saved_stderr = dup( STDERR_FILENO );
if ( saved_stderr < 0 ) { if ( saved_stderr < 0 ) {
perror( "dup" ); perror( "dup" );
return 1; return 1;
@@ -101,7 +101,7 @@ int main( int argc, char *argv[] )
} }
while ( 1 ) { while ( 1 ) {
char buf[ 1024 ]; char buf[1024];
ssize_t bytes_read = read( master, buf, sizeof( buf ) ); ssize_t bytes_read = read( master, buf, sizeof( buf ) );
if ( bytes_read == 0 || ( bytes_read < 0 && errno == EIO ) ) { /* EOF */ if ( bytes_read == 0 || ( bytes_read < 0 && errno == EIO ) ) { /* EOF */
break; break;
+1 -1
View File
@@ -34,7 +34,7 @@
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
int main( int argc __attribute__(( unused )), char **argv __attribute__(( unused ))) int main( int argc __attribute__( ( unused ) ), char** argv __attribute__( ( unused ) ) )
{ {
set_native_locale(); set_native_locale();
if ( !is_utf8_locale() ) { if ( !is_utf8_locale() ) {
+1 -1
View File
@@ -32,8 +32,8 @@
/* Tests that the Mosh network layer seems to be using unique nonces */ /* Tests that the Mosh network layer seems to be using unique nonces */
#include <iostream>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <set> #include <set>
#include "src/network/network.h" #include "src/network/network.h"
+358 -334
View File
@@ -48,49 +48,58 @@
#include "src/util/fatal_assert.h" #include "src/util/fatal_assert.h"
#include "test_utils.h" #include "test_utils.h"
#define KEY_LEN 16 #define KEY_LEN 16
#define NONCE_LEN 12 #define NONCE_LEN 12
#define TAG_LEN 16 #define TAG_LEN 16
using Crypto::AlignedBuffer; using Crypto::AlignedBuffer;
bool verbose = false; bool verbose = false;
static bool equal( const AlignedBuffer &a, const AlignedBuffer &b ) { static bool equal( const AlignedBuffer& a, const AlignedBuffer& b )
return ( a.len() == b.len() ) {
&& !memcmp( a.data(), b.data(), a.len() ); return ( a.len() == b.len() ) && !memcmp( a.data(), b.data(), a.len() );
} }
using AlignedPointer = std::shared_ptr<AlignedBuffer>; using AlignedPointer = std::shared_ptr<AlignedBuffer>;
static AlignedBuffer *get_ctx( const AlignedBuffer &key ) { static AlignedBuffer* get_ctx( const AlignedBuffer& key )
AlignedBuffer *ctx_buf = new AlignedBuffer( ae_ctx_sizeof() ); {
AlignedBuffer* ctx_buf = new AlignedBuffer( ae_ctx_sizeof() );
fatal_assert( ctx_buf ); fatal_assert( ctx_buf );
fatal_assert( AE_SUCCESS == ae_init( (ae_ctx *)ctx_buf->data(), key.data(), key.len(), NONCE_LEN, TAG_LEN ) ); fatal_assert( AE_SUCCESS == ae_init( (ae_ctx*)ctx_buf->data(), key.data(), key.len(), NONCE_LEN, TAG_LEN ) );
return ctx_buf; return ctx_buf;
} }
static void scrap_ctx( AlignedBuffer &ctx_buf ) { static void scrap_ctx( AlignedBuffer& ctx_buf )
fatal_assert( AE_SUCCESS == ae_clear( (ae_ctx *)ctx_buf.data() ) ); {
fatal_assert( AE_SUCCESS == ae_clear( (ae_ctx*)ctx_buf.data() ) );
} }
static void test_encrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, static void test_encrypt( const AlignedBuffer& key,
const AlignedBuffer &plaintext, const AlignedBuffer &assoc, const AlignedBuffer& nonce,
const AlignedBuffer &expected_ciphertext ) { const AlignedBuffer& plaintext,
const AlignedBuffer& assoc,
const AlignedBuffer& expected_ciphertext )
{
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer observed_ciphertext( plaintext.len() + TAG_LEN ); AlignedBuffer observed_ciphertext( plaintext.len() + TAG_LEN );
const int ret = ae_encrypt( ctx, nonce.data(), const int ret = ae_encrypt( ctx,
plaintext.data(), plaintext.len(), nonce.data(),
assoc.data(), assoc.len(), plaintext.data(),
observed_ciphertext.data(), NULL, plaintext.len(),
assoc.data(),
assoc.len(),
observed_ciphertext.data(),
NULL,
AE_FINALIZE ); AE_FINALIZE );
if ( verbose ) { if ( verbose ) {
printf( "ret %d\n", ret ); printf( "ret %d\n", ret );
hexdump(observed_ciphertext, "obs ct" ); hexdump( observed_ciphertext, "obs ct" );
} }
fatal_assert( ret == int( expected_ciphertext.len() ) ); fatal_assert( ret == int( expected_ciphertext.len() ) );
@@ -99,19 +108,26 @@ static void test_encrypt( const AlignedBuffer &key, const AlignedBuffer &nonce,
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
static void test_decrypt( const AlignedBuffer &key, const AlignedBuffer &nonce, static void test_decrypt( const AlignedBuffer& key,
const AlignedBuffer &ciphertext, const AlignedBuffer &assoc, const AlignedBuffer& nonce,
const AlignedBuffer &expected_plaintext, const AlignedBuffer& ciphertext,
bool valid ) { const AlignedBuffer& assoc,
const AlignedBuffer& expected_plaintext,
bool valid )
{
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer observed_plaintext( ciphertext.len() - TAG_LEN ); AlignedBuffer observed_plaintext( ciphertext.len() - TAG_LEN );
const int ret = ae_decrypt( ctx, nonce.data(), const int ret = ae_decrypt( ctx,
ciphertext.data(), ciphertext.len(), nonce.data(),
assoc.data(), assoc.len(), ciphertext.data(),
observed_plaintext.data(), NULL, ciphertext.len(),
assoc.data(),
assoc.len(),
observed_plaintext.data(),
NULL,
AE_FINALIZE ); AE_FINALIZE );
if ( verbose ) { if ( verbose ) {
@@ -131,15 +147,20 @@ static void test_decrypt( const AlignedBuffer &key, const AlignedBuffer &nonce,
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
static void test_vector( const char *key_p, const char *nonce_p, static void test_vector( const char* key_p,
size_t assoc_len, const char *assoc_p, const char* nonce_p,
size_t plaintext_len, const char *plaintext_p, size_t assoc_len,
size_t ciphertext_len, const char *ciphertext_p ) { const char* assoc_p,
size_t plaintext_len,
const char* plaintext_p,
size_t ciphertext_len,
const char* ciphertext_p )
{
AlignedBuffer key ( KEY_LEN, key_p ); AlignedBuffer key( KEY_LEN, key_p );
AlignedBuffer nonce ( NONCE_LEN, nonce_p ); AlignedBuffer nonce( NONCE_LEN, nonce_p );
AlignedBuffer plaintext ( plaintext_len, plaintext_p ); AlignedBuffer plaintext( plaintext_len, plaintext_p );
AlignedBuffer assoc ( assoc_len, assoc_p ); AlignedBuffer assoc( assoc_len, assoc_p );
AlignedBuffer ciphertext( ciphertext_len, ciphertext_p ); AlignedBuffer ciphertext( ciphertext_len, ciphertext_p );
if ( verbose ) { if ( verbose ) {
@@ -150,328 +171,344 @@ static void test_vector( const char *key_p, const char *nonce_p,
hexdump( ciphertext, "exp ct" ); hexdump( ciphertext, "exp ct" );
} }
test_encrypt( key, nonce, plaintext, assoc, test_encrypt( key, nonce, plaintext, assoc, ciphertext );
ciphertext );
test_decrypt( key, nonce, ciphertext, assoc, test_decrypt( key, nonce, ciphertext, assoc, plaintext, true );
plaintext, true );
/* Try some bad ciphertexts and make sure they don't validate. */ /* Try some bad ciphertexts and make sure they don't validate. */
PRNG prng; PRNG prng;
for ( size_t i=0; i<64; i++ ) { for ( size_t i = 0; i < 64; i++ ) {
AlignedBuffer bad_ct( ciphertext.len(), ciphertext.data() ); AlignedBuffer bad_ct( ciphertext.len(), ciphertext.data() );
( (uint8_t *) bad_ct.data() )[ prng.uint32() % bad_ct.len() ] ( (uint8_t*)bad_ct.data() )[prng.uint32() % bad_ct.len()] ^= ( 1 << ( prng.uint8() % 8 ) );
^= ( 1 << ( prng.uint8() % 8 ) ); test_decrypt( key, nonce, bad_ct, assoc, plaintext, false );
test_decrypt( key, nonce, bad_ct, assoc,
plaintext, false );
} }
if (verbose) { if ( verbose ) {
printf( "PASSED\n\n" ); printf( "PASSED\n\n" );
} }
} }
#define TEST_VECTOR( _key, _nonce, _assoc, _pt, _ct ) \ #define TEST_VECTOR( _key, _nonce, _assoc, _pt, _ct ) \
test_vector( _key, _nonce, sizeof(_assoc)-1, _assoc, sizeof(_pt)-1, _pt, sizeof(_ct)-1, _ct ) test_vector( _key, _nonce, sizeof( _assoc ) - 1, _assoc, sizeof( _pt ) - 1, _pt, sizeof( _ct ) - 1, _ct )
static void test_all_vectors( void ) { static void test_all_vectors( void )
{
/* Test vectors from http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A */ /* Test vectors from http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A */
const char ietf_key[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; const char ietf_key[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F";
const char ietf_nonce[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"; const char ietf_nonce[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B";
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" /* associated data */ ietf_nonce,
, "" /* plaintext */ "" /* associated data */
/* ciphertext including tag */ ,
, "\x19\x7B\x9C\x3C\x44\x1D\x3C\x83\xEA\xFB\x2B\xEF\x63\x3B\x91\x82" ); "" /* plaintext */
/* ciphertext including tag */
,
"\x19\x7B\x9C\x3C\x44\x1D\x3C\x83\xEA\xFB\x2B\xEF\x63\x3B\x91\x82" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07" "\x00\x01\x02\x03\x04\x05\x06\x07",
, "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x16\xDC\x76\xA4\x6D\x47\xE1\xEA" "\x00\x01\x02\x03\x04\x05\x06\x07",
"\xD5\x37\x20\x9E\x8A\x96\xD1\x4E" ); "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x16\xDC\x76\xA4\x6D\x47\xE1\xEA"
"\xD5\x37\x20\x9E\x8A\x96\xD1\x4E" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07",
, "\x98\xB9\x15\x52\xC8\xC0\x09\x18\x50\x44\xE3\x0A\x6E\xB2\xFE\x21" ); "",
"\x98\xB9\x15\x52\xC8\xC0\x09\x18\x50\x44\xE3\x0A\x6E\xB2\xFE\x21" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07" "",
, "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x97\x1E\xFF\xCA\xE1\x9A\xD4\x71" "\x00\x01\x02\x03\x04\x05\x06\x07",
"\x6F\x88\xE8\x7B\x87\x1F\xBE\xED" ); "\x92\xB6\x57\x13\x0A\x74\xB8\x5A\x97\x1E\xFF\xCA\xE1\x9A\xD4\x71"
"\x6F\x88\xE8\x7B\x87\x1F\xBE\xED" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"\x77\x6C\x99\x24\xD6\x72\x3A\x1F\xC4\x52\x45\x32\xAC\x3E\x5B\xEB" ); "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x77\x6C\x99\x24\xD6\x72\x3A\x1F\xC4\x52\x45\x32\xAC\x3E\x5B\xEB" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
, "\x7D\xDB\x8E\x6C\xEA\x68\x14\x86\x62\x12\x50\x96\x19\xB1\x9C\xC6" ); "",
"\x7D\xDB\x8E\x6C\xEA\x68\x14\x86\x62\x12\x50\x96\x19\xB1\x9C\xC6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
"\x13\xCC\x8B\x74\x78\x07\x12\x1A\x4C\xBB\x3E\x4B\xD6\xB4\x56\xAF"); "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x13\xCC\x8B\x74\x78\x07\x12\x1A\x4C\xBB\x3E\x4B\xD6\xB4\x56\xAF" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "" "\x10\x11\x12\x13\x14\x15\x16\x17",
, "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" ); "",
"\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" ); "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x5F\xA9\x4F\xC3\xF3\x88\x20\xF1"
"\xDC\x3F\x3D\x1F\xD4\xE5\x5E\x1C" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17" ietf_nonce,
, "" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17",
, "\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6"); "",
"\x28\x20\x26\xDA\x30\x68\xBC\x9F\xA1\x18\x68\x1D\x55\x9F\x10\xF6" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17",
"\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" ); "\xFC\xFC\xEE\x7A\x2A\x8D\x4D\x48\x6E\xF2\xF5\x25\x87\xFD\xA0\xED"
"\x97\xDC\x7E\xED\xE2\x41\xDF\x68" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xB2\xA0\x40\xDD\x3B\xD5\x16\x43\x72\xD7\x6D\x7B\xB6\x82\x42\x40" ); "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\xB2\xA0\x40\xDD\x3B\xD5\x16\x43\x72\xD7\x6D\x7B\xB6\x82\x42\x40" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
, "\xE1\xE0\x72\x63\x3B\xAD\xE5\x1A\x60\xE8\x59\x51\xD9\xC4\x2A\x1B" ); "",
"\xE1\xE0\x72\x63\x3B\xAD\xE5\x1A\x60\xE8\x59\x51\xD9\xC4\x2A\x1B" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "" ietf_nonce,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x4A\x3B\xAE\x82\x44\x65\xCF\xDA\xF8\xC4\x1F\xC5\x0C\x7D\xF9\xD9" ); "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x4A\x3B\xAE\x82\x44\x65\xCF\xDA\xF8\xC4\x1F\xC5\x0C\x7D\xF9\xD9" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" "\x20\x21\x22\x23\x24\x25\x26\x27",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22" "\x20\x21\x22\x23\x24\x25\x26\x27",
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB" "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\x68\xC6\x57\x78\xB0\x58\xA6\x35\x65\x9C\x62\x32\x11\xDE\xEA\x0D" "\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\xE3\x0D\x2C\x38\x18\x79\xF4\xC8" ); "\x68\xC6\x57\x78\xB0\x58\xA6\x35\x65\x9C\x62\x32\x11\xDE\xEA\x0D"
"\xE3\x0D\x2C\x38\x18\x79\xF4\xC8" );
TEST_VECTOR( ietf_key, ietf_nonce TEST_VECTOR( ietf_key,
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" ietf_nonce,
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x20\x21\x22\x23\x24\x25\x26\x27" "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
, "" "\x20\x21\x22\x23\x24\x25\x26\x27",
, "\x7A\xEB\x7A\x69\xA1\x68\x7D\xD0\x82\xCA\x27\xB0\xD9\xA3\x70\x96" ); "",
"\x7A\xEB\x7A\x69\xA1\x68\x7D\xD0\x82\xCA\x27\xB0\xD9\xA3\x70\x96" );
TEST_VECTOR( ietf_key, ietf_nonce
, ""
, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27"
, "\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x68\xC6\x57\x78\xB0\x58\xA6\x35\x06\x0C\x84\x67\xF4\xAB\xAB\x5E"
"\x8B\x3C\x20\x67\xA2\xE1\x15\xDC" );
TEST_VECTOR( ietf_key,
ietf_nonce,
"",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
"\x20\x21\x22\x23\x24\x25\x26\x27",
"\xBE\xA5\xE8\x79\x8D\xBE\x71\x10\x03\x1C\x14\x4D\xA0\xB2\x61\x22"
"\xCE\xAA\xB9\xB0\x5D\xF7\x71\xA6\x57\x14\x9D\x53\x77\x34\x63\xCB"
"\x68\xC6\x57\x78\xB0\x58\xA6\x35\x06\x0C\x84\x67\xF4\xAB\xAB\x5E"
"\x8B\x3C\x20\x67\xA2\xE1\x15\xDC" );
/* Some big texts. These were originally encrypted using this program; /* Some big texts. These were originally encrypted using this program;
they are regression tests. */ they are regression tests. */
TEST_VECTOR( TEST_VECTOR( "\x06\xF8\x9F\x69\xDA\x49\xDA\xD7\x68\x48\xFF\xB3\x60\xB6\x8F\x00",
"\x06\xF8\x9F\x69\xDA\x49\xDA\xD7\x68\x48\xFF\xB3\x60\xB6\x8F\x00" "\xDC\xBF\x85\x18\x23\xD9\x67\x85\x45\x59\x6F\xAD",
, "\xDC\xBF\x85\x18\x23\xD9\x67\x85\x45\x59\x6F\xAD" "",
, "" "\xC4\x8E\x1F\x04\x10\x2F\xA5\x58\x68\x42\x62\xF3\x1B\xE7\x63\xA7"
, "\xC4\x8E\x1F\x04\x10\x2F\xA5\x58\x68\x42\x62\xF3\x1B\xE7\x63\xA7" "\x77\x89\x64\x16\xE6\xB0\xF7\xFA\xFE\xF0\xB9\x50\x22\xDC\xCE\x78"
"\x77\x89\x64\x16\xE6\xB0\xF7\xFA\xFE\xF0\xB9\x50\x22\xDC\xCE\x78" "\xA5\x01\xA4\x2D\xA2\x0F\x50\xEA\x9A\xAE\x23\x60\x1C\xC9\x11\x84"
"\xA5\x01\xA4\x2D\xA2\x0F\x50\xEA\x9A\xAE\x23\x60\x1C\xC9\x11\x84" "\x5F\xD0\x0A\x88\x99\xCD\xF1\x1B\x7C\xF9\x71\xC2\xD8\xE3\x7B\xB1"
"\x5F\xD0\x0A\x88\x99\xCD\xF1\x1B\x7C\xF9\x71\xC2\xD8\xE3\x7B\xB1" "\xBC\x91\xCE\x10\xDA\xED\xBF\xA5\xDF\x96\x69\x7A\xFA\xCA\x9A\x91"
"\xBC\x91\xCE\x10\xDA\xED\xBF\xA5\xDF\x96\x69\x7A\xFA\xCA\x9A\x91" "\x40\xF9\x8F\xF1\xFE\x5A\x2B\x21\x17\xA5\xAE\x97\xBD\x0B\x68\xE0"
"\x40\xF9\x8F\xF1\xFE\x5A\x2B\x21\x17\xA5\xAE\x97\xBD\x0B\x68\xE0" "\x58\x7A\xB4\x10\x89\xDE\x30\x12\xB7\x14\xEB\xE8\xFA\xA5\x87\x7A"
"\x58\x7A\xB4\x10\x89\xDE\x30\x12\xB7\x14\xEB\xE8\xFA\xA5\x87\x7A" "\x7E\xEA\xD0\x34\x0B\xAF\x73\x0B\x1C\xA7\xC5\xCB\x84\x15\x5F\x45"
"\x7E\xEA\xD0\x34\x0B\xAF\x73\x0B\x1C\xA7\xC5\xCB\x84\x15\x5F\x45" "\x53\x8E\x27\xD7\xA0\x7D\xAF\x99\x05\x6E\x9F\xF4\xCD\xE5\xA1\x11"
"\x53\x8E\x27\xD7\xA0\x7D\xAF\x99\x05\x6E\x9F\xF4\xCD\xE5\xA1\x11" "\x43\xBD\x7A\x24\xA3\xB7\xB6\x92\x7C\xB7\xD8\x6F\x17\x78\xB3\x30"
"\x43\xBD\x7A\x24\xA3\xB7\xB6\x92\x7C\xB7\xD8\x6F\x17\x78\xB3\x30" "\x53\x6E\x46\x14\x11\x86\xF6\x70\x2E\x25\x8E\x54\xF8\x70\x2F\x47"
"\x53\x6E\x46\x14\x11\x86\xF6\x70\x2E\x25\x8E\x54\xF8\x70\x2F\x47" "\x2B\xF8\xF6\xC3\xEE\xFB\x9B\x7D\xF0\x73\xAD\x24\xC0\xE9\x58\xA8"
"\x2B\xF8\xF6\xC3\xEE\xFB\x9B\x7D\xF0\x73\xAD\x24\xC0\xE9\x58\xA8" "\x79\xDC\x92\x25\x6D\xF3\x9D\xA8\x8F\x07\xA3\xE7\xAB\xF4\xBC\x32"
"\x79\xDC\x92\x25\x6D\xF3\x9D\xA8\x8F\x07\xA3\xE7\xAB\xF4\xBC\x32" "\x29\xB6\xEF\xDB\x20\x42\x51\xCF\xD5\x57\xDA\x14\xC7\x2A\xB3\x19"
"\x29\xB6\xEF\xDB\x20\x42\x51\xCF\xD5\x57\xDA\x14\xC7\x2A\xB3\x19" "\xDE\x31\xF7\x77\x95\x8D\x33\x57\x1D\xEB\x9A\x80\x98\x4D\x95\x07"
"\xDE\x31\xF7\x77\x95\x8D\x33\x57\x1D\xEB\x9A\x80\x98\x4D\x95\x07" "\xEB\x38\x42\xEB\xE0\xAB\x2D\xF3\x9E\x16\xFA\x6D\x06\x62\x41\x58"
"\xEB\x38\x42\xEB\xE0\xAB\x2D\xF3\x9E\x16\xFA\x6D\x06\x62\x41\x58" "\x81\xBF\x3D\x01\x1A\xF7\xBA\x53\xCE\x60\xEF\x86\xC9\x11\xED\x89"
"\x81\xBF\x3D\x01\x1A\xF7\xBA\x53\xCE\x60\xEF\x86\xC9\x11\xED\x89" "\x5B\xCF\x98\xB4\xD3\xD4\xAC\xB9\x71\x69\xEE\xF6\xA7\x77\x4E\x46"
"\x5B\xCF\x98\xB4\xD3\xD4\xAC\xB9\x71\x69\xEE\xF6\xA7\x77\x4E\x46" "\x0F\xF7\x1D\x7D\x36\x4E\x86\x15\xCD\xF3\xD3\x5C\xAA\x97\x74\xCC"
"\x0F\xF7\x1D\x7D\x36\x4E\x86\x15\xCD\xF3\xD3\x5C\xAA\x97\x74\xCC" "\x5D\xD6\xF0\x5C\x62\x27\x0D\x4B\x01\xDD\x8B\xE6\x2C\x72\x99\xBA"
"\x5D\xD6\xF0\x5C\x62\x27\x0D\x4B\x01\xDD\x8B\xE6\x2C\x72\x99\xBA" "\x5B\x95\x87\x0F\xB9\x9A\xBE\xA2\x42\x5E\x7D\x1B\x19\x08\xBC\xEF"
"\x5B\x95\x87\x0F\xB9\x9A\xBE\xA2\x42\x5E\x7D\x1B\x19\x08\xBC\xEF" "\x66\x24\x2F\xB3\xFC\xE7\xDA\xB1\x79\x51\xE9\x3B\x45\x66\xDC\xD8"
"\x66\x24\x2F\xB3\xFC\xE7\xDA\xB1\x79\x51\xE9\x3B\x45\x66\xDC\xD8" "\x0F\xA7\x56\xBF\x56\x15\x4B\x15\xCC\x3E\x9F\x44\xB9\xED\xC2\x0A"
"\x0F\xA7\x56\xBF\x56\x15\x4B\x15\xCC\x3E\x9F\x44\xB9\xED\xC2\x0A" "\x7C\xA9\x6D\x87\xCD\xE2\x00\x9F\x2F\x2C\xF0\xBC\x1B\xB0\x27\xE8"
"\x7C\xA9\x6D\x87\xCD\xE2\x00\x9F\x2F\x2C\xF0\xBC\x1B\xB0\x27\xE8" "\x46\x3E\x06\x1E\xD5\xB6\xB4\x82\xF7\x88\xB7\x48\xF3\x58\xB0\x23"
"\x46\x3E\x06\x1E\xD5\xB6\xB4\x82\xF7\x88\xB7\x48\xF3\x58\xB0\x23" "\x6D\xC2\xD3\x85\x9D\x1F\xE5\x30\xFC\xC8\x46\x62\xD1\xE1\xAE\x3B"
"\x6D\xC2\xD3\x85\x9D\x1F\xE5\x30\xFC\xC8\x46\x62\xD1\xE1\xAE\x3B" "\x3A\xA9\x57\xE0\xD7\x8F\x7E\x4D\x59\x7D\x7A\xEB\xBF\xD3\x10\x00"
"\x3A\xA9\x57\xE0\xD7\x8F\x7E\x4D\x59\x7D\x7A\xEB\xBF\xD3\x10\x00" "\xA0\xE4\x76\x76\xE3\xA8\x14\xD0\x03\xBF\x1F\x6A\xF9\x11\xCE\x98"
"\xA0\xE4\x76\x76\xE3\xA8\x14\xD0\x03\xBF\x1F\x6A\xF9\x11\xCE\x98" "\x2C\x2A\x86\x25\x77\x85\x14\x76\xD4\x51\xAB\xC7\x3A\xA7\xE1\xF7"
"\x2C\x2A\x86\x25\x77\x85\x14\x76\xD4\x51\xAB\xC7\x3A\xA7\xE1\xF7" "\x23\xF7\x2B\xA3\xBA\xE4\x0B\xA4\x81\x9A\x83\x98\x69\xC3\x1C\x8A"
"\x23\xF7\x2B\xA3\xBA\xE4\x0B\xA4\x81\x9A\x83\x98\x69\xC3\x1C\x8A" "\xBD\x26\x12\x36\x22\x9D\xCE\x85\x5D\xA3\xA0\xDF\x66\xD0\x59\xF6"
"\xBD\x26\x12\x36\x22\x9D\xCE\x85\x5D\xA3\xA0\xDF\x66\xD0\x59\xF6" "\x47\xF2\xC5\x37\xF1\x62\x0D\x0C\x45\x5B\xE5\xFE\x3C\x8D\x28\x75",
"\x47\xF2\xC5\x37\xF1\x62\x0D\x0C\x45\x5B\xE5\xFE\x3C\x8D\x28\x75" "\xa1\xd8\xa0\xe0\x75\x5c\xb4\xf4\xab\x59\x6d\x14\xfc\x2e\x75\x54"
, "\xa1\xd8\xa0\xe0\x75\x5c\xb4\xf4\xab\x59\x6d\x14\xfc\x2e\x75\x54" "\xa3\x35\x4f\x57\x69\x48\x7a\x46\x17\x5f\xd9\x34\x50\xf9\x35\xe5"
"\xa3\x35\x4f\x57\x69\x48\x7a\x46\x17\x5f\xd9\x34\x50\xf9\x35\xe5" "\x6f\xee\x27\xdb\x28\x0f\x06\x0b\xaf\xd5\x50\x4e\x20\x78\x35\xd6"
"\x6f\xee\x27\xdb\x28\x0f\x06\x0b\xaf\xd5\x50\x4e\x20\x78\x35\xd6" "\x4d\xa0\x18\xe8\x6c\x5b\x07\xbb\xb6\xd0\x3f\x4a\x0e\x14\x32\xaf"
"\x4d\xa0\x18\xe8\x6c\x5b\x07\xbb\xb6\xd0\x3f\x4a\x0e\x14\x32\xaf" "\x0d\x6a\x5d\xb6\xe4\x36\xb0\x1c\xae\x2e\x75\x85\x19\x53\x9d\xf2"
"\x0d\x6a\x5d\xb6\xe4\x36\xb0\x1c\xae\x2e\x75\x85\x19\x53\x9d\xf2" "\xc6\xc6\xf5\x29\x4a\x5d\x73\xd6\xcd\x3a\xec\x38\x15\x7b\x1f\x3d"
"\xc6\xc6\xf5\x29\x4a\x5d\x73\xd6\xcd\x3a\xec\x38\x15\x7b\x1f\x3d" "\x19\x8f\x7f\x85\x53\x75\xfe\x6c\x8a\x5d\x4f\x05\x3f\x16\x5e\x73"
"\x19\x8f\x7f\x85\x53\x75\xfe\x6c\x8a\x5d\x4f\x05\x3f\x16\x5e\x73" "\x9c\xbe\xbe\xdb\xe8\xb2\x3e\xea\x0f\x2e\x2c\xf3\x2c\x9c\x56\x5b"
"\x9c\xbe\xbe\xdb\xe8\xb2\x3e\xea\x0f\x2e\x2c\xf3\x2c\x9c\x56\x5b" "\xf7\xf2\xed\x87\x07\xfb\x87\x74\x09\xda\xb8\xad\x57\xc0\xa0\x79"
"\xf7\xf2\xed\x87\x07\xfb\x87\x74\x09\xda\xb8\xad\x57\xc0\xa0\x79" "\xc6\x69\x1c\x79\x08\x69\x35\x18\xc4\x54\xca\x6b\xed\x89\xa0\x27"
"\xc6\x69\x1c\x79\x08\x69\x35\x18\xc4\x54\xca\x6b\xed\x89\xa0\x27" "\x32\x19\x1f\xaf\xff\x12\x1c\x1b\x08\xa6\x9f\x0e\xe3\x01\x98\x77"
"\x32\x19\x1f\xaf\xff\x12\x1c\x1b\x08\xa6\x9f\x0e\xe3\x01\x98\x77" "\xdb\x75\xdf\x87\x67\x6d\xd9\xb4\x23\x39\xd0\x81\x54\xf8\x89\x18"
"\xdb\x75\xdf\x87\x67\x6d\xd9\xb4\x23\x39\xd0\x81\x54\xf8\x89\x18" "\x5c\xbd\xb7\x4d\xf4\xb1\x8b\x37\x2e\x9b\x20\x29\x9f\x95\x9e\xdd"
"\x5c\xbd\xb7\x4d\xf4\xb1\x8b\x37\x2e\x9b\x20\x29\x9f\x95\x9e\xdd" "\x73\x2c\xdb\x37\xe5\x7d\x9f\x8a\x79\xff\x8f\x5b\x99\x10\xe1\xe9"
"\x73\x2c\xdb\x37\xe5\x7d\x9f\x8a\x79\xff\x8f\x5b\x99\x10\xe1\xe9" "\x1d\xcb\x1f\x17\xef\x7d\xb7\xd1\x50\x23\x4a\x38\x92\xfb\xbe\x1c"
"\x1d\xcb\x1f\x17\xef\x7d\xb7\xd1\x50\x23\x4a\x38\x92\xfb\xbe\x1c" "\xff\x23\x68\x3a\xb9\xc8\xbc\xa0\xab\xa7\xbe\xc8\x84\xce\x66\xa7"
"\xff\x23\x68\x3a\xb9\xc8\xbc\xa0\xab\xa7\xbe\xc8\x84\xce\x66\xa7" "\x11\x17\xc6\x48\x91\x4d\x77\xe1\x64\xc4\x26\x08\xcd\xb4\xea\x10"
"\x11\x17\xc6\x48\x91\x4d\x77\xe1\x64\xc4\x26\x08\xcd\xb4\xea\x10" "\x42\x60\x5a\x7c\x5a\x72\xba\xb4\x93\xf7\x02\xa1\xd4\x44\xdb\x1a"
"\x42\x60\x5a\x7c\x5a\x72\xba\xb4\x93\xf7\x02\xa1\xd4\x44\xdb\x1a" "\x4a\xc3\xb1\xea\x69\x74\xea\x18\xb3\x5a\x27\x09\x6f\x5b\x1f\x30"
"\x4a\xc3\xb1\xea\x69\x74\xea\x18\xb3\x5a\x27\x09\x6f\x5b\x1f\x30" "\xe2\xeb\x2a\x37\x5e\xd2\xe2\x23\xec\xbc\xcb\xcb\x65\x41\xed\x0e"
"\xe2\xeb\x2a\x37\x5e\xd2\xe2\x23\xec\xbc\xcb\xcb\x65\x41\xed\x0e" "\x23\xda\x71\x45\xf3\x3f\x7d\x44\x73\xd7\x14\x4d\xab\x03\xf7\x7d"
"\x23\xda\x71\x45\xf3\x3f\x7d\x44\x73\xd7\x14\x4d\xab\x03\xf7\x7d" "\x24\x33\x9d\x25\x83\x1f\x3d\xf3\xc9\x34\x42\x6c\x2f\x61\xa2\x83"
"\x24\x33\x9d\x25\x83\x1f\x3d\xf3\xc9\x34\x42\x6c\x2f\x61\xa2\x83" "\xa0\x0f\xb6\x28\xe8\x27\xf0\x91\xf6\xc5\x7e\xa1\x74\x74\xaf\xa7"
"\xa0\x0f\xb6\x28\xe8\x27\xf0\x91\xf6\xc5\x7e\xa1\x74\x74\xaf\xa7" "\x7f\xba\x99\x29\xb2\x91\x9f\xf0\xf7\x71\x1f\xf0\x0b\xd6\xd9\xf9"
"\x7f\xba\x99\x29\xb2\x91\x9f\xf0\xf7\x71\x1f\xf0\x0b\xd6\xd9\xf9" "\x72\xaa\x04\x09\x5d\x75\x76\x5f\x18\xc8\xd5\xd5\x84\x1a\x40\x3f"
"\x72\xaa\x04\x09\x5d\x75\x76\x5f\x18\xc8\xd5\xd5\x84\x1a\x40\x3f" "\xe5\x38\xe4\x38\xdf\xf9\x0d\x79\xdc\x71\xc8\xa8\xef\x86\x4e\x26"
"\xe5\x38\xe4\x38\xdf\xf9\x0d\x79\xdc\x71\xc8\xa8\xef\x86\x4e\x26" "\xf4\xc2\x46\xf4\xe0\x64\xd6\x5e\xfb\x1c\x47\x58\x3d\x87\x7c\xba"
"\xf4\xc2\x46\xf4\xe0\x64\xd6\x5e\xfb\x1c\x47\x58\x3d\x87\x7c\xba" "\xa3\xf5\x98\xdc\xd5\xdf\xaf\x62\x96\xee\x4e\x39\x32\x4c\x6d\xd2"
"\xa3\xf5\x98\xdc\xd5\xdf\xaf\x62\x96\xee\x4e\x39\x32\x4c\x6d\xd2" "\x0a\x6f\xf8\x34\x98\x76\xae\x21\xa1\x41\x3d\x96\xfc\x52\xdb\xdd"
"\x0a\x6f\xf8\x34\x98\x76\xae\x21\xa1\x41\x3d\x96\xfc\x52\xdb\xdd" "\xf3\x9a\xb1\xf3\x78\x3d\xb8\x2f\xae\xae\x7d\xe0\x4b\xb2\xdf\x2b"
"\xf3\x9a\xb1\xf3\x78\x3d\xb8\x2f\xae\xae\x7d\xe0\x4b\xb2\xdf\x2b" "\x12\xac\xee\xfb\xf8\x54\x60\xea\x74\xd5\xde\x43\x7b\x38\x45\x5f"
"\x12\xac\xee\xfb\xf8\x54\x60\xea\x74\xd5\xde\x43\x7b\x38\x45\x5f" "\xa0\xb8\xe8\xcc\x8a\xe3\xcc\x0c\x92\xe6\xb1\xb0\xe2\xc1\x99\xca"
"\xa0\xb8\xe8\xcc\x8a\xe3\xcc\x0c\x92\xe6\xb1\xb0\xe2\xc1\x99\xca" "\x1b\xa1\xac\x6e\x7a\x8a\xa0\x20\x3d\xeb\x29\x8b\xf4\x55\x41\x62" );
"\x1b\xa1\xac\x6e\x7a\x8a\xa0\x20\x3d\xeb\x29\x8b\xf4\x55\x41\x62" );
TEST_VECTOR( TEST_VECTOR( "\x7A\x54\x0D\x3E\x56\x38\xF7\xC6\xCF\xAB\xF9\x56\xDC\xCA\x14\x23",
"\x7A\x54\x0D\x3E\x56\x38\xF7\xC6\xCF\xAB\xF9\x56\xDC\xCA\x14\x23" "\x9B\x0E\xC1\x15\xD5\xE6\xC9\xAB\xE6\x88\x2A\x18",
, "\x9B\x0E\xC1\x15\xD5\xE6\xC9\xAB\xE6\x88\x2A\x18" "",
, "" "\x52\xDB\xA7\x44\x2B\x1C\x9C\x24\x4D\xF3\xA1\xE4\x53\x7B\x9B\xB2"
, "\x52\xDB\xA7\x44\x2B\x1C\x9C\x24\x4D\xF3\xA1\xE4\x53\x7B\x9B\xB2" "\x25\xC5\xA3\x81\x42\x23\xA9\xB4\x12\xF8\xFC\xE4\xF6\x8E\x20\xD4"
"\x25\xC5\xA3\x81\x42\x23\xA9\xB4\x12\xF8\xFC\xE4\xF6\x8E\x20\xD4" "\x59\x7B\x39\x2D\x5D\x7C\x6E\xB7\x51\x02\x90\x7A\x8E\xAA\x30\xD0"
"\x59\x7B\x39\x2D\x5D\x7C\x6E\xB7\x51\x02\x90\x7A\x8E\xAA\x30\xD0" "\xEB\xDF\x70\x09\x5A\xEC\xFB\xD4\xDB\x0B\xE9\x1B\x79\xAF\x40\xA3"
"\xEB\xDF\x70\x09\x5A\xEC\xFB\xD4\xDB\x0B\xE9\x1B\x79\xAF\x40\xA3" "\xC9\xD1\x3F\x0E\x7E\x9B\xA8\xF2\x4E\xA2\xAE\x81\xE7\x8F\xE9\x4A"
"\xC9\xD1\x3F\x0E\x7E\x9B\xA8\xF2\x4E\xA2\xAE\x81\xE7\x8F\xE9\x4A" "\xF8\x84\x38\x20\x9B\xCB\x0D\x9E\x46\x87\xCC\x0C\x71\xF2\x4C\xE5"
"\xF8\x84\x38\x20\x9B\xCB\x0D\x9E\x46\x87\xCC\x0C\x71\xF2\x4C\xE5" "\x57\x47\xFB\x52\x1D\x0C\x92\xCD\x9B\xD0\xA3\xA3\xFE\x44\x4F\x2A"
"\x57\x47\xFB\x52\x1D\x0C\x92\xCD\x9B\xD0\xA3\xA3\xFE\x44\x4F\x2A" "\x2D\x7D\xC8\x48\x87\x71\x8A\x83\x83\x43\x52\xD9\x87\x72\x0A\x1F"
"\x2D\x7D\xC8\x48\x87\x71\x8A\x83\x83\x43\x52\xD9\x87\x72\x0A\x1F" "\x72\x7D\xE0\x0D\xE4\xBA\x38\x2D\xAB\x89\x85\x34\xFE\x30\xE9\x47"
"\x72\x7D\xE0\x0D\xE4\xBA\x38\x2D\xAB\x89\x85\x34\xFE\x30\xE9\x47" "\x99\x69\x96\x37\x3B\x88\x0D\x08\x5B\x41\xF0\x55\x3A\x90\xB6\x5D"
"\x99\x69\x96\x37\x3B\x88\x0D\x08\x5B\x41\xF0\x55\x3A\x90\xB6\x5D" "\xC9\xB8\xFA\x22\xEB\x54\x67\x1B\x43\x3B\x2A\x13\xDF\x76\x71\xBF"
"\xC9\xB8\xFA\x22\xEB\x54\x67\x1B\x43\x3B\x2A\x13\xDF\x76\x71\xBF" "\x8F\xBA\xF6\x5F\xE0\xAE\x84\x80\xD3\x9A\x9E\xFE\x35\x29\x0C\x44"
"\x8F\xBA\xF6\x5F\xE0\xAE\x84\x80\xD3\x9A\x9E\xFE\x35\x29\x0C\x44" "\xB8\x3B\xC8\x58\x3F\x51\x18\xF0\x8E\x3F\xE0\xFE\xFF\x55\xE1\xFD"
"\xB8\x3B\xC8\x58\x3F\x51\x18\xF0\x8E\x3F\xE0\xFE\xFF\x55\xE1\xFD" "\x79\xED\x71\x6D\xB9\xBE\xF0\x76\x25\x4E\x6F\xA2\x49\x5B\xEC\x20"
"\x79\xED\x71\x6D\xB9\xBE\xF0\x76\x25\x4E\x6F\xA2\x49\x5B\xEC\x20" "\x61\x95\x65\x67\xBF\xA3\x73\x18\x65\x7C\x2F\x94\x29\x3B\x57\x7D"
"\x61\x95\x65\x67\xBF\xA3\x73\x18\x65\x7C\x2F\x94\x29\x3B\x57\x7D" "\x78\x6A\xBD\x22\x2B\x17\x5E\xDA\x2B\x4E\xC3\xA5\xF0\x9A\xD9\x1C"
"\x78\x6A\xBD\x22\x2B\x17\x5E\xDA\x2B\x4E\xC3\xA5\xF0\x9A\xD9\x1C" "\xCF\xC4\xDD\x7E\x07\xB4\xE6\x50\xD0\x9D\xAF\x86\x9D\x3F\xFE\x70"
"\xCF\xC4\xDD\x7E\x07\xB4\xE6\x50\xD0\x9D\xAF\x86\x9D\x3F\xFE\x70" "\x64\x18\xBA\x32\x90\xE5\x63\xA0\x08\x3D\x1D\xCE\xB2\x77\x31\x59"
"\x64\x18\xBA\x32\x90\xE5\x63\xA0\x08\x3D\x1D\xCE\xB2\x77\x31\x59" "\xD4\xB4\x03\x2C\xE2\x15\x11\x17\xD1\xBA\x5E\x1E\x7D\x38\x09\xCF"
"\xD4\xB4\x03\x2C\xE2\x15\x11\x17\xD1\xBA\x5E\x1E\x7D\x38\x09\xCF" "\x25\x3E\x3D\x89\x7E\x13\xAE\x11\xFA\xCD\x46\x23\x86\xC5\x78\xC8"
"\x25\x3E\x3D\x89\x7E\x13\xAE\x11\xFA\xCD\x46\x23\x86\xC5\x78\xC8" "\x1A\xE8\x8F\x9A\x9C\xCE\xBE\x01\x5D\x70\x56\xA8\x84\x07\x07\x2A"
"\x1A\xE8\x8F\x9A\x9C\xCE\xBE\x01\x5D\x70\x56\xA8\x84\x07\x07\x2A" "\x1C\xD4\x33\x8B\x2F\x01\x0F\xD0\xCC\xCD\x29\xEC\x73\x6E\xDE\xB2"
"\x1C\xD4\x33\x8B\x2F\x01\x0F\xD0\xCC\xCD\x29\xEC\x73\x6E\xDE\xB2" "\xBC\xA7\x32\x6A\x12\x93\x13\xCD\xE0\xA9\x55\x97\x36\x6B\xDA\xC5"
"\xBC\xA7\x32\x6A\x12\x93\x13\xCD\xE0\xA9\x55\x97\x36\x6B\xDA\xC5" "\xC5\xD2\x44\x80\xBD\x00\x37\xD7\x21\x80\x27\x1F\x2E\x02\x76\x1A"
"\xC5\xD2\x44\x80\xBD\x00\x37\xD7\x21\x80\x27\x1F\x2E\x02\x76\x1A" "\x57\xA5\x5F\xCE\x8B\x20\x6A\x6A\x0F\x70\x9B\xDB\x07\x64\x96\x7B"
"\x57\xA5\x5F\xCE\x8B\x20\x6A\x6A\x0F\x70\x9B\xDB\x07\x64\x96\x7B" "\x43\xFD\xCA\xBF\x9D\x52\xEB\x24\x7B\x1B\xEE\x43\x10\x2C\xDB\x92"
"\x43\xFD\xCA\xBF\x9D\x52\xEB\x24\x7B\x1B\xEE\x43\x10\x2C\xDB\x92" "\xA1\xA8\xA9\xF7\x2F\xD2\x39\xA8\x85\x9C\xFE\x2C\x2A\xCF\x52\x73"
"\xA1\xA8\xA9\xF7\x2F\xD2\x39\xA8\x85\x9C\xFE\x2C\x2A\xCF\x52\x73" "\xFB\xCA\x20\xAD\xC9\xDD\xFC\x4A\x91\x39\x6C\x7C\x84\x67\xC5\xE4"
"\xFB\xCA\x20\xAD\xC9\xDD\xFC\x4A\x91\x39\x6C\x7C\x84\x67\xC5\xE4" "\x9B\x3E\x3D\x6B\x56\x3B\x2B\xDC\x8A\x46\xF6\x7C\x36\xF9\x27\x29"
"\x9B\x3E\x3D\x6B\x56\x3B\x2B\xDC\x8A\x46\xF6\x7C\x36\xF9\x27\x29" "\x37\x38\x7C\x9D\xA0\x6E\x5D\x4C\xE5\xB2\x6F\x0C\xDC\xEF\xFE\x35"
"\x37\x38\x7C\x9D\xA0\x6E\x5D\x4C\xE5\xB2\x6F\x0C\xDC\xEF\xFE\x35" "\xFE\x3D\x56\x40\x7F\xBD\x4D\xDD\x40\x79\xDD\xA7\x0A\x7B\xA2\xCE"
"\xFE\x3D\x56\x40\x7F\xBD\x4D\xDD\x40\x79\xDD\xA7\x0A\x7B\xA2\xCE" "\x22\x38\x94\xEA\x90\xF5\x95\xB6\xE6\x6F\x14\xFB\xA2",
"\x22\x38\x94\xEA\x90\xF5\x95\xB6\xE6\x6F\x14\xFB\xA2" "\xec\xa9\xcc\x30\x66\x6c\x04\x16\x21\x8d\xc8\x15\x47\xa2\x18\xcf"
, "\xec\xa9\xcc\x30\x66\x6c\x04\x16\x21\x8d\xc8\x15\x47\xa2\x18\xcf" "\x19\x90\x4f\x82\x27\x25\xa2\x1e\xfa\x1c\xe4\x58\x78\x43\x52\x4c"
"\x19\x90\x4f\x82\x27\x25\xa2\x1e\xfa\x1c\xe4\x58\x78\x43\x52\x4c" "\xac\x24\xde\xcb\xad\x80\x05\x7a\xeb\x2d\xc0\x33\x05\x31\x25\x44"
"\xac\x24\xde\xcb\xad\x80\x05\x7a\xeb\x2d\xc0\x33\x05\x31\x25\x44" "\xd7\x11\xa1\xf2\xcb\x09\x6f\xf0\x14\x3c\x3f\xf2\xc7\x79\xfb\x3f"
"\xd7\x11\xa1\xf2\xcb\x09\x6f\xf0\x14\x3c\x3f\xf2\xc7\x79\xfb\x3f" "\xb0\x0a\x65\xae\xd7\xe5\x5d\x35\x0c\xb4\x69\x7a\x89\x6b\xa9\xdc"
"\xb0\x0a\x65\xae\xd7\xe5\x5d\x35\x0c\xb4\x69\x7a\x89\x6b\xa9\xdc" "\x02\x69\x96\xd2\x9f\xe7\x3c\x99\xd4\xd3\x55\x97\xc4\x59\xad\xc4"
"\x02\x69\x96\xd2\x9f\xe7\x3c\x99\xd4\xd3\x55\x97\xc4\x59\xad\xc4" "\x0c\x7b\xf8\x47\x1c\xbe\x36\x2a\x53\x6b\xb1\x21\x5f\xc1\x6e\xca"
"\x0c\x7b\xf8\x47\x1c\xbe\x36\x2a\x53\x6b\xb1\x21\x5f\xc1\x6e\xca" "\x0a\x4f\x16\x3a\xf0\xd4\x12\xa6\xf8\x68\x9f\x12\xad\x36\x4c\xd8"
"\x0a\x4f\x16\x3a\xf0\xd4\x12\xa6\xf8\x68\x9f\x12\xad\x36\x4c\xd8" "\x5a\x5b\x17\xb8\xbd\xc7\x2c\x48\x51\x7d\x45\x74\x00\xb0\x02\xe9"
"\x5a\x5b\x17\xb8\xbd\xc7\x2c\x48\x51\x7d\x45\x74\x00\xb0\x02\xe9" "\x1b\xd6\x0c\x41\xa7\x5d\x65\xca\x68\xa7\x3e\x3c\xf5\xaa\x9b\xbd"
"\x1b\xd6\x0c\x41\xa7\x5d\x65\xca\x68\xa7\x3e\x3c\xf5\xaa\x9b\xbd" "\x25\x98\x00\xd8\x4d\xbc\xd1\x7a\x25\x34\x92\x24\xa4\x84\x62\x63"
"\x25\x98\x00\xd8\x4d\xbc\xd1\x7a\x25\x34\x92\x24\xa4\x84\x62\x63" "\x2c\x40\xa5\x58\x81\x90\xf1\x0f\x75\xaa\x70\xe4\x4e\x0f\xa3\x03"
"\x2c\x40\xa5\x58\x81\x90\xf1\x0f\x75\xaa\x70\xe4\x4e\x0f\xa3\x03" "\x90\xd1\x07\x18\x0a\x50\x9a\x3e\x28\x1f\x33\xb7\x11\xed\x3c\x2c"
"\x90\xd1\x07\x18\x0a\x50\x9a\x3e\x28\x1f\x33\xb7\x11\xed\x3c\x2c" "\x40\xc8\xd7\xe3\x12\xdc\xef\x94\x93\x7b\x11\xc3\x24\x51\x61\xbf"
"\x40\xc8\xd7\xe3\x12\xdc\xef\x94\x93\x7b\x11\xc3\x24\x51\x61\xbf" "\x8b\xa4\xa4\x5c\x85\x0d\x50\x49\x45\x69\xbe\x5b\x36\x90\x84\x30"
"\x8b\xa4\xa4\x5c\x85\x0d\x50\x49\x45\x69\xbe\x5b\x36\x90\x84\x30" "\x66\x67\x76\x3d\xcc\x02\x8b\x9f\xb7\x90\x57\xef\xe1\x21\x34\x65"
"\x66\x67\x76\x3d\xcc\x02\x8b\x9f\xb7\x90\x57\xef\xe1\x21\x34\x65" "\x3e\xca\xbf\x70\x1d\x76\x63\xbf\xae\x1f\xb2\x55\xf0\x87\x3e\x42"
"\x3e\xca\xbf\x70\x1d\x76\x63\xbf\xae\x1f\xb2\x55\xf0\x87\x3e\x42" "\xf9\x71\x28\x02\x06\x9e\xf7\x6a\x47\x3b\xda\x38\x54\x66\xd9\xaf"
"\xf9\x71\x28\x02\x06\x9e\xf7\x6a\x47\x3b\xda\x38\x54\x66\xd9\xaf" "\xba\x7b\xec\xbf\xe3\x52\x63\x02\x8b\xa7\xad\x1d\x76\x16\xa2\x20"
"\xba\x7b\xec\xbf\xe3\x52\x63\x02\x8b\xa7\xad\x1d\x76\x16\xa2\x20" "\x38\xec\x40\xb7\xc8\x35\x6b\xc2\x80\x9d\x20\x02\xc6\x34\xdb\x65"
"\x38\xec\x40\xb7\xc8\x35\x6b\xc2\x80\x9d\x20\x02\xc6\x34\xdb\x65" "\xd8\x27\x0b\xc5\x2d\x85\xe4\xdc\x85\xae\x10\x36\x01\xdb\x4b\xaf"
"\xd8\x27\x0b\xc5\x2d\x85\xe4\xdc\x85\xae\x10\x36\x01\xdb\x4b\xaf" "\x44\x79\xea\x23\x21\xa0\x83\xa3\x91\xf5\xc5\x16\x9b\xeb\x43\x92"
"\x44\x79\xea\x23\x21\xa0\x83\xa3\x91\xf5\xc5\x16\x9b\xeb\x43\x92" "\x1f\x88\xd2\x00\x60\x40\xe9\x52\x0b\x39\x86\x3b\x9e\x3b\x9a\x4a"
"\x1f\x88\xd2\x00\x60\x40\xe9\x52\x0b\x39\x86\x3b\x9e\x3b\x9a\x4a" "\x31\xdf\xb6\x57\x78\x38\xcf\x77\x7c\x0c\xf4\x14\x90\x25\xed\x27"
"\x31\xdf\xb6\x57\x78\x38\xcf\x77\x7c\x0c\xf4\x14\x90\x25\xed\x27" "\xd2\x86\x20\x4c\x1a\x52\xeb\xbe\x1e\xac\x2b\xce\xb7\x72\x86\x87"
"\xd2\x86\x20\x4c\x1a\x52\xeb\xbe\x1e\xac\x2b\xce\xb7\x72\x86\x87" "\xfd\xac\x11\x90\xc5\xea\x96\xcb\xdc\x89\xe9\x77\xf0\x83\xc3\xa7"
"\xfd\xac\x11\x90\xc5\xea\x96\xcb\xdc\x89\xe9\x77\xf0\x83\xc3\xa7" "\xa7\xd1\xe1\xc9\x7e\x89\xb3\x4e\xf1\x12\xa3\x9c\xfe\x66\xcc\x5d"
"\xa7\xd1\xe1\xc9\x7e\x89\xb3\x4e\xf1\x12\xa3\x9c\xfe\x66\xcc\x5d" "\xcf\x1d\x5a\x11\x21\x2f\x10\x66\x37\x5f\xd7\x35\xeb\x09\x62\x99"
"\xcf\x1d\x5a\x11\x21\x2f\x10\x66\x37\x5f\xd7\x35\xeb\x09\x62\x99" "\xa6\xf8\xc7\xc7\xef\xd3\xf0\x56\x2b\xa7\x14\x65\x6a\xce\xa9\x68"
"\xa6\xf8\xc7\xc7\xef\xd3\xf0\x56\x2b\xa7\x14\x65\x6a\xce\xa9\x68" "\xe7\xa4\x89\xb4\x1e\x16\x99\xbf\x8d\x2d\x5e\x67\xb4\x3a\x0b\xf3"
"\xe7\xa4\x89\xb4\x1e\x16\x99\xbf\x8d\x2d\x5e\x67\xb4\x3a\x0b\xf3" "\x37\x14\x1e\x5d\xc6\xb4\xb5\x9e\xa5\x69\xa4\xaf\xcc\x0f\x46\xe9"
"\x37\x14\x1e\x5d\xc6\xb4\xb5\x9e\xa5\x69\xa4\xaf\xcc\x0f\x46\xe9" "\xd5\xbb\x10\x49\x07\x0d\x92\x42\x0c\x04\xb9\xdf\xa4\xb5\xef\xcc"
"\xd5\xbb\x10\x49\x07\x0d\x92\x42\x0c\x04\xb9\xdf\xa4\xb5\xef\xcc" "\x05\x81\x3f\xc1\x21\x12\x2c\x33\xb7\x79\xfd\x5d\x8a" );
"\x05\x81\x3f\xc1\x21\x12\x2c\x33\xb7\x79\xfd\x5d\x8a" );
} }
/* http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A /* http://tools.ietf.org/html/draft-krovetz-ocb-03#appendix-A
also specifies an iterative test algorithm, which we implement here. */ also specifies an iterative test algorithm, which we implement here. */
static void test_iterative( void ) { static void test_iterative( void )
{
/* Key is always all zeros */ /* Key is always all zeros */
AlignedBuffer key( KEY_LEN ); AlignedBuffer key( KEY_LEN );
memset( key.data(), 0, KEY_LEN ); memset( key.data(), 0, KEY_LEN );
AlignedPointer ctx_buf( get_ctx( key ) ); AlignedPointer ctx_buf( get_ctx( key ) );
ae_ctx *ctx = (ae_ctx *)ctx_buf->data(); ae_ctx* ctx = (ae_ctx*)ctx_buf->data();
AlignedBuffer nonce( NONCE_LEN ); AlignedBuffer nonce( NONCE_LEN );
memset( nonce.data(), 0, NONCE_LEN ); memset( nonce.data(), 0, NONCE_LEN );
@@ -479,43 +516,32 @@ static void test_iterative( void ) {
/* The loop below fills this buffer in order. /* The loop below fills this buffer in order.
Each iteration adds 2*i + 3*TAG_LEN bytes. */ Each iteration adds 2*i + 3*TAG_LEN bytes. */
AlignedBuffer accumulator( 22400 ); AlignedBuffer accumulator( 22400 );
uint8_t *acc = (uint8_t *) accumulator.data(); uint8_t* acc = (uint8_t*)accumulator.data();
for ( size_t i=0; i<128; i++ ) { for ( size_t i = 0; i < 128; i++ ) {
/* i bytes of zeros */ /* i bytes of zeros */
AlignedBuffer s( i ); AlignedBuffer s( i );
memset( s.data(), 0, s.len() ); memset( s.data(), 0, s.len() );
/* Nonce is 11 bytes of zeros followed by 1 byte i */ /* Nonce is 11 bytes of zeros followed by 1 byte i */
( (uint8_t *) nonce.data() )[ 11 ] = i; ( (uint8_t*)nonce.data() )[11] = i;
/* We can't write directly to acc because it might not be aligned. */ /* We can't write directly to acc because it might not be aligned. */
AlignedBuffer out( s.len() + TAG_LEN ); AlignedBuffer out( s.len() + TAG_LEN );
/* OCB-ENCRYPT(K,N,S,S) */ /* OCB-ENCRYPT(K,N,S,S) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert(
s.data(), s.len(), 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) );
s.data(), s.len(),
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), s.len() + TAG_LEN ); memcpy( acc, out.data(), s.len() + TAG_LEN );
acc += s.len() + TAG_LEN; acc += s.len() + TAG_LEN;
/* OCB-ENCRYPT(K,N,<empty string>,S) */ /* OCB-ENCRYPT(K,N,<empty string>,S) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), s.data(), s.len(), NULL, 0, out.data(), NULL, AE_FINALIZE ) );
s.data(), s.len(),
NULL, 0,
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), s.len() + TAG_LEN ); memcpy( acc, out.data(), s.len() + TAG_LEN );
acc += s.len() + TAG_LEN; acc += s.len() + TAG_LEN;
/* OCB-ENCRYPT(K,N,S,<empty string>) */ /* OCB-ENCRYPT(K,N,S,<empty string>) */
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), NULL, 0, s.data(), s.len(), out.data(), NULL, AE_FINALIZE ) );
NULL, 0,
s.data(), s.len(),
out.data(), NULL,
AE_FINALIZE ) );
memcpy( acc, out.data(), TAG_LEN ); memcpy( acc, out.data(), TAG_LEN );
acc += TAG_LEN; acc += TAG_LEN;
} }
@@ -523,11 +549,9 @@ static void test_iterative( void ) {
/* OCB-ENCRYPT(K,N,C,<empty string>) */ /* OCB-ENCRYPT(K,N,C,<empty string>) */
AlignedBuffer out( TAG_LEN ); AlignedBuffer out( TAG_LEN );
memset( nonce.data(), 0, NONCE_LEN ); memset( nonce.data(), 0, NONCE_LEN );
fatal_assert( 0 <= ae_encrypt( ctx, nonce.data(), fatal_assert(
NULL, 0, 0 <= ae_encrypt(
accumulator.data(), accumulator.len(), ctx, nonce.data(), NULL, 0, accumulator.data(), accumulator.len(), out.data(), NULL, AE_FINALIZE ) );
out.data(), NULL,
AE_FINALIZE ) );
/* Check this final tag against the known value */ /* Check this final tag against the known value */
AlignedBuffer correct( TAG_LEN, "\xB2\xB4\x1C\xBF\x9B\x05\x03\x7D\xA7\xF1\x6C\x24\xA3\x5C\x1C\x94" ); AlignedBuffer correct( TAG_LEN, "\xB2\xB4\x1C\xBF\x9B\x05\x03\x7D\xA7\xF1\x6C\x24\xA3\x5C\x1C\x94" );
@@ -539,16 +563,16 @@ static void test_iterative( void ) {
scrap_ctx( *ctx_buf ); scrap_ctx( *ctx_buf );
} }
int main( int argc, char *argv[] ) int main( int argc, char* argv[] )
{ {
if ( argc >= 2 && strcmp( argv[ 1 ], "-v" ) == 0 ) { if ( argc >= 2 && strcmp( argv[1], "-v" ) == 0 ) {
verbose = true; verbose = true;
} }
try { try {
test_all_vectors(); test_all_vectors();
test_iterative(); test_iterative();
} catch ( const std::exception &e ) { } catch ( const std::exception& e ) {
fprintf( stderr, "Error: %s\r\n", e.what() ); fprintf( stderr, "Error: %s\r\n", e.what() );
return 1; return 1;
} }
+8 -5
View File
@@ -34,22 +34,25 @@
#include "test_utils.h" #include "test_utils.h"
void hexdump( const void *buf, size_t len, const char *name ) { void hexdump( const void* buf, size_t len, const char* name )
const unsigned char *data = (const unsigned char *) buf; {
const unsigned char* data = (const unsigned char*)buf;
printf( DUMP_NAME_FMT, name ); printf( DUMP_NAME_FMT, name );
for ( size_t i = 0; i < len; i++ ) { for ( size_t i = 0; i < len; i++ ) {
// Although data[i] is an unsigned char, it will be promoted to a signed int // Although data[i] is an unsigned char, it will be promoted to a signed int
// when passed as an argument. Explicitly cast it back to an unsigned type // when passed as an argument. Explicitly cast it back to an unsigned type
// so it can be printed in hex. // so it can be printed in hex.
printf( "%02x", static_cast<unsigned>( data[ i ] ) ); printf( "%02x", static_cast<unsigned>( data[i] ) );
} }
printf( "\n" ); printf( "\n" );
} }
void hexdump( const Crypto::AlignedBuffer &buf, const char *name ) { void hexdump( const Crypto::AlignedBuffer& buf, const char* name )
{
hexdump( buf.data(), buf.len(), name ); hexdump( buf.data(), buf.len(), name );
} }
void hexdump( const std::string &buf, const char *name ) { void hexdump( const std::string& buf, const char* name )
{
hexdump( buf.data(), buf.size(), name ); hexdump( buf.data(), buf.size(), name );
} }
+3 -3
View File
@@ -39,8 +39,8 @@
#define DUMP_NAME_FMT "%-10s " #define DUMP_NAME_FMT "%-10s "
void hexdump( const void *buf, size_t len, const char *name ); void hexdump( const void* buf, size_t len, const char* name );
void hexdump( const Crypto::AlignedBuffer &buf, const char *name ); void hexdump( const Crypto::AlignedBuffer& buf, const char* name );
void hexdump( const std::string &buf, const char *name ); void hexdump( const std::string& buf, const char* name );
#endif #endif
+10 -8
View File
@@ -38,17 +38,19 @@
#include "src/crypto/crypto.h" #include "src/crypto/crypto.h"
static void dos_detected( const char *expression, const char *file, int line, const char *function ) static void dos_detected( const char* expression, const char* file, int line, const char* function )
{ {
char buffer[ 2048 ]; char buffer[2048];
snprintf( buffer, 2048, "Illegal counterparty input (possible denial of service) in function %s at %s:%d, failed test: %s\n", snprintf( buffer,
function, file, line, expression ); 2048,
"Illegal counterparty input (possible denial of service) in function %s at %s:%d, failed test: %s\n",
function,
file,
line,
expression );
throw Crypto::CryptoException( buffer ); throw Crypto::CryptoException( buffer );
} }
#define dos_assert(expr) \ #define dos_assert( expr ) ( ( expr ) ? (void)0 : dos_detected( #expr, __FILE__, __LINE__, __func__ ) )
((expr) \
? (void)0 \
: dos_detected (#expr, __FILE__, __LINE__, __func__ ))
#endif #endif
+8 -7
View File
@@ -36,16 +36,17 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
static void fatal_error( const char *expression, const char *file, int line, const char *function ) static void fatal_error( const char* expression, const char* file, int line, const char* function )
{ {
fprintf( stderr, "Fatal assertion failure in function %s at %s:%d\nFailed test: %s\n", fprintf( stderr,
function, file, line, expression ); "Fatal assertion failure in function %s at %s:%d\nFailed test: %s\n",
function,
file,
line,
expression );
abort(); abort();
} }
#define fatal_assert(expr) \ #define fatal_assert( expr ) ( ( expr ) ? (void)0 : fatal_error( #expr, __FILE__, __LINE__, __func__ ) )
((expr) \
? (void)0 \
: fatal_error (#expr, __FILE__, __LINE__, __func__ ))
#endif #endif
+13 -13
View File
@@ -45,7 +45,6 @@
#include "src/util/locale_utils.h" #include "src/util/locale_utils.h"
const std::string LocaleVar::str( void ) const const std::string LocaleVar::str( void ) const
{ {
if ( name.empty() ) { if ( name.empty() ) {
@@ -57,22 +56,22 @@ const std::string LocaleVar::str( void ) const
const LocaleVar get_ctype( void ) const LocaleVar get_ctype( void )
{ {
/* Reimplement the search logic, just for diagnostics */ /* Reimplement the search logic, just for diagnostics */
if ( const char *all = getenv( "LC_ALL" ) ) { if ( const char* all = getenv( "LC_ALL" ) ) {
return LocaleVar( "LC_ALL", all ); return LocaleVar( "LC_ALL", all );
} else if ( const char *ctype = getenv( "LC_CTYPE" ) ) { } else if ( const char* ctype = getenv( "LC_CTYPE" ) ) {
return LocaleVar( "LC_CTYPE", ctype ); return LocaleVar( "LC_CTYPE", ctype );
} else if ( const char *lang = getenv( "LANG" ) ) { } else if ( const char* lang = getenv( "LANG" ) ) {
return LocaleVar( "LANG", lang ); return LocaleVar( "LANG", lang );
} }
return LocaleVar( "", "" ); return LocaleVar( "", "" );
} }
const char *locale_charset( void ) const char* locale_charset( void )
{ {
static const char ASCII_name[] = "US-ASCII"; static const char ASCII_name[] = "US-ASCII";
/* Produce more pleasant name of US-ASCII */ /* Produce more pleasant name of US-ASCII */
const char *ret = nl_langinfo( CODESET ); const char* ret = nl_langinfo( CODESET );
if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) { if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) {
ret = ASCII_name; ret = ASCII_name;
@@ -81,16 +80,17 @@ const char *locale_charset( void )
return ret; return ret;
} }
bool is_utf8_locale( void ) { bool is_utf8_locale( void )
{
/* Verify locale calls for UTF-8 */ /* Verify locale calls for UTF-8 */
if ( strcmp( locale_charset(), "UTF-8" ) != 0 && if ( strcmp( locale_charset(), "UTF-8" ) != 0 && strcmp( locale_charset(), "utf-8" ) != 0 ) {
strcmp( locale_charset(), "utf-8" ) != 0 ) {
return false; return false;
} }
return true; return true;
} }
void set_native_locale( void ) { void set_native_locale( void )
{
/* Adopt native locale */ /* Adopt native locale */
if ( NULL == setlocale( LC_ALL, "" ) ) { if ( NULL == setlocale( LC_ALL, "" ) ) {
int saved_errno = errno; int saved_errno = errno;
@@ -98,8 +98,7 @@ void set_native_locale( void ) {
LocaleVar ctype( get_ctype() ); LocaleVar ctype( get_ctype() );
fprintf( stderr, "The locale requested by %s isn't available here.\n", ctype.str().c_str() ); fprintf( stderr, "The locale requested by %s isn't available here.\n", ctype.str().c_str() );
if ( !ctype.name.empty() ) { if ( !ctype.name.empty() ) {
fprintf( stderr, "Running `locale-gen %s' may be necessary.\n\n", fprintf( stderr, "Running `locale-gen %s' may be necessary.\n\n", ctype.value.c_str() );
ctype.value.c_str() );
} }
} else { } else {
errno = saved_errno; errno = saved_errno;
@@ -108,7 +107,8 @@ void set_native_locale( void ) {
} }
} }
void clear_locale_variables( void ) { void clear_locale_variables( void )
{
unsetenv( "LANG" ); unsetenv( "LANG" );
unsetenv( "LANGUAGE" ); unsetenv( "LANGUAGE" );
unsetenv( "LC_CTYPE" ); unsetenv( "LC_CTYPE" );
+5 -6
View File
@@ -35,17 +35,16 @@
#include <string> #include <string>
class LocaleVar { class LocaleVar
public: {
public:
const std::string name, value; const std::string name, value;
LocaleVar( const char *s_name, const char *s_value ) LocaleVar( const char* s_name, const char* s_value ) : name( s_name ), value( s_value ) {}
: name( s_name ), value( s_value )
{}
const std::string str( void ) const; const std::string str( void ) const;
}; };
const LocaleVar get_ctype( void ); const LocaleVar get_ctype( void );
const char *locale_charset( void ); const char* locale_charset( void );
bool is_utf8_locale( void ); bool is_utf8_locale( void );
void set_native_locale( void ); void set_native_locale( void );
void clear_locale_variables( void ); void clear_locale_variables( void );
+37 -41
View File
@@ -32,7 +32,7 @@
#include "src/include/config.h" #include "src/include/config.h"
#if !defined(HAVE_FORKPTY) || !defined(HAVE_CFMAKERAW) #if !defined( HAVE_FORKPTY ) || !defined( HAVE_CFMAKERAW )
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -46,13 +46,11 @@
#include "src/util/pty_compat.h" #include "src/util/pty_compat.h"
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
pid_t my_forkpty( int *amaster, char *name, pid_t my_forkpty( int* amaster, char* name, const struct termios* termp, const struct winsize* winp )
const struct termios *termp,
const struct winsize *winp )
{ {
/* For Solaris and AIX */ /* For Solaris and AIX */
int master, slave; int master, slave;
char *slave_name; char* slave_name;
pid_t pid; pid_t pid;
#ifdef _AIX #ifdef _AIX
@@ -73,7 +71,7 @@ pid_t my_forkpty( int *amaster, char *name,
return -1; return -1;
} }
if ( unlockpt(master) < 0 ) { if ( unlockpt( master ) < 0 ) {
perror( "unlockpt" ); perror( "unlockpt" );
close( master ); close( master );
return -1; return -1;
@@ -94,8 +92,7 @@ pid_t my_forkpty( int *amaster, char *name,
} }
#ifndef _AIX #ifndef _AIX
if ( ioctl(slave, I_PUSH, "ptem") < 0 || if ( ioctl( slave, I_PUSH, "ptem" ) < 0 || ioctl( slave, I_PUSH, "ldterm" ) < 0 ) {
ioctl(slave, I_PUSH, "ldterm") < 0 ) {
perror( "ioctl(I_PUSH)" ); perror( "ioctl(I_PUSH)" );
close( slave ); close( slave );
close( master ); close( master );
@@ -106,7 +103,7 @@ pid_t my_forkpty( int *amaster, char *name,
if ( amaster != NULL ) if ( amaster != NULL )
*amaster = master; *amaster = master;
if ( name != NULL) if ( name != NULL )
strcpy( name, slave_name ); strcpy( name, slave_name );
if ( termp != NULL ) { if ( termp != NULL ) {
@@ -122,7 +119,7 @@ pid_t my_forkpty( int *amaster, char *name,
w.ws_col = 80; w.ws_col = 80;
w.ws_xpixel = 0; w.ws_xpixel = 0;
w.ws_ypixel = 0; w.ws_ypixel = 0;
if ( ioctl( slave, TIOCSWINSZ, &w) < 0 ) { if ( ioctl( slave, TIOCSWINSZ, &w ) < 0 ) {
perror( "ioctl TIOCSWINSZ" ); perror( "ioctl TIOCSWINSZ" );
exit( 1 ); exit( 1 );
} }
@@ -135,51 +132,50 @@ pid_t my_forkpty( int *amaster, char *name,
pid = fork(); pid = fork();
switch ( pid ) { switch ( pid ) {
case -1: /* Error */ case -1: /* Error */
perror( "fork()" ); perror( "fork()" );
return -1;
case 0: /* Child */
if ( setsid() < 0 )
perror( "setsid" );
#ifdef TIOCSCTTY
if ( ioctl( slave, TIOCSCTTY, NULL ) < 0 ) {
perror( "ioctl" );
return -1; return -1;
} case 0: /* Child */
if ( setsid() < 0 )
perror( "setsid" );
#ifdef TIOCSCTTY
if ( ioctl( slave, TIOCSCTTY, NULL ) < 0 ) {
perror( "ioctl" );
return -1;
}
#else #else
{ {
int dummy_fd; int dummy_fd;
dummy_fd = open (slave_name, O_RDWR); dummy_fd = open( slave_name, O_RDWR );
if (dummy_fd < 0) { if ( dummy_fd < 0 ) {
perror( "open(slave_name)" ); perror( "open(slave_name)" );
return -1; return -1;
} }
close (dummy_fd); close( dummy_fd );
} }
#endif /* TIOCSCTTY */ #endif /* TIOCSCTTY */
close( master ); close( master );
dup2( slave, STDIN_FILENO ); dup2( slave, STDIN_FILENO );
dup2( slave, STDOUT_FILENO ); dup2( slave, STDOUT_FILENO );
dup2( slave, STDERR_FILENO ); dup2( slave, STDERR_FILENO );
return 0; return 0;
default: /* Parent */ default: /* Parent */
close( slave ); close( slave );
return pid; return pid;
} }
} }
#endif #endif
#ifndef HAVE_CFMAKERAW #ifndef HAVE_CFMAKERAW
void my_cfmakeraw( struct termios *termios_p ) void my_cfmakeraw( struct termios* termios_p )
{ {
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP termios_p->c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON );
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST; termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); termios_p->c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
termios_p->c_cflag &= ~(CSIZE | PARENB); termios_p->c_cflag &= ~( CSIZE | PARENB );
termios_p->c_cflag |= CS8; termios_p->c_cflag |= CS8;
termios_p->c_cc[VMIN] = 1; // read() is satisfied after 1 char termios_p->c_cc[VMIN] = 1; // read() is satisfied after 1 char
termios_p->c_cc[VTIME] = 0; // No timer termios_p->c_cc[VTIME] = 0; // No timer
} }
#endif #endif
+4 -6
View File
@@ -36,16 +36,14 @@
#include "src/include/config.h" #include "src/include/config.h"
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
# define forkpty my_forkpty #define forkpty my_forkpty
#endif #endif
#ifndef HAVE_CFMAKERAW #ifndef HAVE_CFMAKERAW
# define cfmakeraw my_cfmakeraw #define cfmakeraw my_cfmakeraw
#endif #endif
pid_t my_forkpty( int *amaster, char *name, pid_t my_forkpty( int* amaster, char* name, const struct termios* termp, const struct winsize* winp );
const struct termios *termp,
const struct winsize *winp );
void my_cfmakeraw( struct termios *termios_p ); void my_cfmakeraw( struct termios* termios_p );
#endif #endif
+2 -2
View File
@@ -43,6 +43,6 @@ void Select::handle_signal( int signum )
fatal_assert( signum >= 0 ); fatal_assert( signum >= 0 );
fatal_assert( signum <= MAX_SIGNAL_NUMBER ); fatal_assert( signum <= MAX_SIGNAL_NUMBER );
Select &sel = get_instance(); Select& sel = get_instance();
sel.got_signal[ signum ] = 1; sel.got_signal[signum] = 1;
} }
+35 -36
View File
@@ -48,9 +48,11 @@
Any signals blocked by calling sigprocmask() outside this code will still be Any signals blocked by calling sigprocmask() outside this code will still be
received during Select::select(). So don't do that. */ received during Select::select(). So don't do that. */
class Select { class Select
{
public: public:
static Select &get_instance( void ) { static Select& get_instance( void )
{
/* COFU may or may not be thread-safe, depending on compiler */ /* COFU may or may not be thread-safe, depending on compiler */
static Select instance; static Select instance;
return instance; return instance;
@@ -59,12 +61,10 @@ public:
private: private:
Select() Select()
: max_fd( -1 ) : max_fd( -1 )
/* These initializations are not used; they are just /* These initializations are not used; they are just
here to appease -Weffc++. */ here to appease -Weffc++. */
, all_fds( dummy_fd_set ) ,
, read_fds( dummy_fd_set ) all_fds( dummy_fd_set ), read_fds( dummy_fd_set ), empty_sigset( dummy_sigset ), consecutive_polls( 0 )
, empty_sigset( dummy_sigset )
, consecutive_polls( 0 )
{ {
FD_ZERO( &all_fds ); FD_ZERO( &all_fds );
FD_ZERO( &read_fds ); FD_ZERO( &read_fds );
@@ -75,16 +75,15 @@ private:
void clear_got_signal( void ) void clear_got_signal( void )
{ {
for ( volatile sig_atomic_t *p = got_signal; for ( volatile sig_atomic_t* p = got_signal; p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
p < got_signal + sizeof( got_signal ) / sizeof( *got_signal );
p++ ) { p++ ) {
*p = 0; *p = 0;
} }
} }
/* not implemented */ /* not implemented */
Select( const Select & ); Select( const Select& );
Select &operator=( const Select & ); Select& operator=( const Select& );
public: public:
void add_fd( int fd ) void add_fd( int fd )
@@ -95,10 +94,7 @@ public:
FD_SET( fd, &all_fds ); FD_SET( fd, &all_fds );
} }
void clear_fds( void ) void clear_fds( void ) { FD_ZERO( &all_fds ); }
{
FD_ZERO( &all_fds );
}
static void add_signal( int signum ) static void add_signal( int signum )
{ {
@@ -123,7 +119,7 @@ public:
/* timeout unit: milliseconds; negative timeout means wait forever */ /* timeout unit: milliseconds; negative timeout means wait forever */
int select( int timeout ) int select( int timeout )
{ {
memcpy( &read_fds, &all_fds, sizeof( read_fds ) ); memcpy( &read_fds, &all_fds, sizeof( read_fds ) );
clear_got_signal(); clear_got_signal();
/* Rate-limit and warn about polls. */ /* Rate-limit and warn about polls. */
@@ -132,35 +128,35 @@ public:
} }
if ( timeout == 0 && ++consecutive_polls >= MAX_POLLS ) { if ( timeout == 0 && ++consecutive_polls >= MAX_POLLS ) {
if ( verbose > 1 && consecutive_polls == MAX_POLLS ) { if ( verbose > 1 && consecutive_polls == MAX_POLLS ) {
fprintf( stderr, "%s: got %d polls, rate limiting.\n", __func__, MAX_POLLS ); fprintf( stderr, "%s: got %d polls, rate limiting.\n", __func__, MAX_POLLS );
} }
timeout = 1; timeout = 1;
} else if ( timeout != 0 && consecutive_polls ) { } else if ( timeout != 0 && consecutive_polls ) {
if ( verbose > 1 && consecutive_polls >= MAX_POLLS ) { if ( verbose > 1 && consecutive_polls >= MAX_POLLS ) {
fprintf( stderr, "%s: got %d consecutive polls\n", __func__, consecutive_polls ); fprintf( stderr, "%s: got %d consecutive polls\n", __func__, consecutive_polls );
} }
consecutive_polls = 0; consecutive_polls = 0;
} }
#ifdef HAVE_PSELECT #ifdef HAVE_PSELECT
struct timespec ts; struct timespec ts;
struct timespec *tsp = NULL; struct timespec* tsp = NULL;
if ( timeout >= 0 ) { if ( timeout >= 0 ) {
ts.tv_sec = timeout / 1000; ts.tv_sec = timeout / 1000;
ts.tv_nsec = 1000000 * (long( timeout ) % 1000); ts.tv_nsec = 1000000 * ( long( timeout ) % 1000 );
tsp = &ts; tsp = &ts;
} }
int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset ); int ret = ::pselect( max_fd + 1, &read_fds, NULL, NULL, tsp, &empty_sigset );
#else #else
struct timeval tv; struct timeval tv;
struct timeval *tvp = NULL; struct timeval* tvp = NULL;
sigset_t old_sigset; sigset_t old_sigset;
if ( timeout >= 0 ) { if ( timeout >= 0 ) {
tv.tv_sec = timeout / 1000; tv.tv_sec = timeout / 1000;
tv.tv_usec = 1000 * (long( timeout ) % 1000); tv.tv_usec = 1000 * ( long( timeout ) % 1000 );
tvp = &tv; tvp = &tv;
} }
@@ -174,11 +170,11 @@ public:
if ( ret == 0 || ( ret == -1 && errno == EINTR ) ) { if ( ret == 0 || ( ret == -1 && errno == EINTR ) ) {
/* Look for and report Cygwin select() bug. */ /* Look for and report Cygwin select() bug. */
if ( ret == 0 ) { if ( ret == 0 ) {
for ( int fd = 0; fd <= max_fd; fd++ ) { for ( int fd = 0; fd <= max_fd; fd++ ) {
if ( FD_ISSET( fd, &read_fds ) ) { if ( FD_ISSET( fd, &read_fds ) ) {
fprintf( stderr, "select(): nfds = 0 but read fd %d is set\n", fd ); fprintf( stderr, "select(): nfds = 0 but read fd %d is set\n", fd );
} }
} }
} }
/* The user should process events as usual. */ /* The user should process events as usual. */
FD_ZERO( &read_fds ); FD_ZERO( &read_fds );
@@ -205,8 +201,8 @@ public:
fatal_assert( signum >= 0 ); fatal_assert( signum >= 0 );
fatal_assert( signum <= MAX_SIGNAL_NUMBER ); fatal_assert( signum <= MAX_SIGNAL_NUMBER );
/* XXX This requires a guard against concurrent signals. */ /* XXX This requires a guard against concurrent signals. */
bool rv = got_signal[ signum ]; bool rv = got_signal[signum];
got_signal[ signum ] = 0; got_signal[signum] = 0;
return rv; return rv;
} }
@@ -214,13 +210,16 @@ public:
bool any_signal( void ) const bool any_signal( void ) const
{ {
bool rv = false; bool rv = false;
for (int i = 0; i < MAX_SIGNAL_NUMBER; i++) { for ( int i = 0; i < MAX_SIGNAL_NUMBER; i++ ) {
rv |= got_signal[ i ]; rv |= got_signal[i];
} }
return rv; return rv;
} }
static void set_verbose( unsigned int s_verbose ) { verbose = s_verbose; } static void set_verbose( unsigned int s_verbose )
{
verbose = s_verbose;
}
private: private:
static const int MAX_SIGNAL_NUMBER = 64; static const int MAX_SIGNAL_NUMBER = 64;
@@ -234,7 +233,7 @@ private:
/* We assume writes to got_signal are atomic, though we also try to mask out /* We assume writes to got_signal are atomic, though we also try to mask out
concurrent signal handlers. */ concurrent signal handlers. */
volatile sig_atomic_t got_signal[ MAX_SIGNAL_NUMBER + 1 ]; volatile sig_atomic_t got_signal[MAX_SIGNAL_NUMBER + 1];
fd_set all_fds, read_fds; fd_set all_fds, read_fds;
+3 -4
View File
@@ -37,13 +37,12 @@
#include "src/util/swrite.h" #include "src/util/swrite.h"
int swrite( int fd, const char *str, ssize_t len ) int swrite( int fd, const char* str, ssize_t len )
{ {
ssize_t total_bytes_written = 0; ssize_t total_bytes_written = 0;
ssize_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t) strlen( str ); ssize_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t)strlen( str );
while ( total_bytes_written < bytes_to_write ) { while ( total_bytes_written < bytes_to_write ) {
ssize_t bytes_written = write( fd, str + total_bytes_written, ssize_t bytes_written = write( fd, str + total_bytes_written, bytes_to_write - total_bytes_written );
bytes_to_write - total_bytes_written );
if ( bytes_written <= 0 ) { if ( bytes_written <= 0 ) {
perror( "write" ); perror( "write" );
return -1; return -1;
+1 -1
View File
@@ -33,6 +33,6 @@
#ifndef SWRITE_HPP #ifndef SWRITE_HPP
#define SWRITE_HPP #define SWRITE_HPP
int swrite( int fd, const char *str, ssize_t len = -1 ); int swrite( int fd, const char* str, ssize_t len = -1 );
#endif #endif
+11 -11
View File
@@ -44,15 +44,15 @@
#include <mach/mach_time.h> #include <mach/mach_time.h>
#endif #endif
#if HAVE_GETTIMEOFDAY #if HAVE_GETTIMEOFDAY
#include <sys/time.h>
#include <cstdio> #include <cstdio>
#include <sys/time.h>
#endif #endif
// On Apple systems CLOCK_MONOTONIC is unfortunately able to go // On Apple systems CLOCK_MONOTONIC is unfortunately able to go
// backwards in time. This breaks mosh when system is returning from // backwards in time. This breaks mosh when system is returning from
// suspend as described in ticket #1014. To avoid this issue prefer // suspend as described in ticket #1014. To avoid this issue prefer
// CLOCK_MONOTONIC_RAW on Apple systems when available. // CLOCK_MONOTONIC_RAW on Apple systems when available.
#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW) #if defined( __APPLE__ ) && defined( CLOCK_MONOTONIC_RAW )
#define CLOCKTYPE CLOCK_MONOTONIC_RAW #define CLOCKTYPE CLOCK_MONOTONIC_RAW
#else #else
#define CLOCKTYPE CLOCK_MONOTONIC #define CLOCKTYPE CLOCK_MONOTONIC
@@ -79,11 +79,11 @@ void freeze_timestamp( void )
struct timespec tp; struct timespec tp;
if ( if (
#if defined(__APPLE__) && defined(__MACH__) #if defined( __APPLE__ ) && defined( __MACH__ )
// Check for presence, for OS X SDK >= 10.12 and runtime < 10.12 // Check for presence, for OS X SDK >= 10.12 and runtime < 10.12
&clock_gettime != NULL && &clock_gettime != NULL &&
#endif #endif
clock_gettime( CLOCKTYPE, &tp ) == 0 ) { clock_gettime( CLOCKTYPE, &tp ) == 0 ) {
uint64_t millis = tp.tv_nsec / 1000000; uint64_t millis = tp.tv_nsec / 1000000;
millis += uint64_t( tp.tv_sec ) * 1000; millis += uint64_t( tp.tv_sec ) * 1000;
@@ -97,8 +97,8 @@ void freeze_timestamp( void )
static mach_timebase_info_data_t s_timebase_info; static mach_timebase_info_data_t s_timebase_info;
static double absolute_to_millis = 0.0; static double absolute_to_millis = 0.0;
if (absolute_to_millis == 0.0) { if ( absolute_to_millis == 0.0 ) {
if (ERR_SUCCESS == mach_timebase_info(&s_timebase_info)) { if ( ERR_SUCCESS == mach_timebase_info( &s_timebase_info ) ) {
absolute_to_millis = 1e-6 * s_timebase_info.numer / s_timebase_info.denom; absolute_to_millis = 1e-6 * s_timebase_info.numer / s_timebase_info.denom;
} else } else
absolute_to_millis = -1.0; absolute_to_millis = -1.0;
@@ -106,7 +106,7 @@ void freeze_timestamp( void )
// NB: mach_absolute_time() returns "absolute time units" // NB: mach_absolute_time() returns "absolute time units"
// We need to apply a conversion to get milliseconds. // We need to apply a conversion to get milliseconds.
if (absolute_to_millis > 0.0) { if ( absolute_to_millis > 0.0 ) {
millis_cache = mach_absolute_time() * absolute_to_millis; millis_cache = mach_absolute_time() * absolute_to_millis;
return; return;
} }
@@ -115,7 +115,7 @@ void freeze_timestamp( void )
// Not monotonic. // Not monotonic.
// NOTE: If time steps backwards, timeouts may be confused. // NOTE: If time steps backwards, timeouts may be confused.
struct timeval tv; struct timeval tv;
if ( gettimeofday(&tv, NULL) ) { if ( gettimeofday( &tv, NULL ) ) {
perror( "gettimeofday" ); perror( "gettimeofday" );
} else { } else {
uint64_t millis = tv.tv_usec / 1000; uint64_t millis = tv.tv_usec / 1000;
@@ -125,6 +125,6 @@ void freeze_timestamp( void )
return; return;
} }
#else #else
# error "gettimeofday() unavailable-- required as timer of last resort" #error "gettimeofday() unavailable-- required as timer of last resort"
#endif #endif
} }