Line data Source code
1 : /* 2 : Mosh: the mobile shell 3 : Copyright 2012 Keith Winstein 4 : 5 : This program is free software: you can redistribute it and/or modify 6 : it under the terms of the GNU General Public License as published by 7 : the Free Software Foundation, either version 3 of the License, or 8 : (at your option) any later version. 9 : 10 : This program is distributed in the hope that it will be useful, 11 : but WITHOUT ANY WARRANTY; without even the implied warranty of 12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 : GNU General Public License for more details. 14 : 15 : You should have received a copy of the GNU General Public License 16 : along with this program. If not, see <http://www.gnu.org/licenses/>. 17 : 18 : In addition, as a special exception, the copyright holders give 19 : permission to link the code of portions of this program with the 20 : OpenSSL library under certain conditions as described in each 21 : individual source file, and distribute linked combinations including 22 : the two. 23 : 24 : You must obey the GNU General Public License in all respects for all 25 : of the code used other than OpenSSL. If you modify file(s) with this 26 : exception, you may extend this exception to your version of the 27 : file(s), but you are not obligated to do so. If you do not wish to do 28 : so, delete this exception statement from your version. If you delete 29 : this exception statement from all source files in the program, then 30 : also delete it here. 31 : */ 32 : 33 : #include <assert.h> 34 : 35 : #include "byteorder.h" 36 : #include "transportfragment.h" 37 : #include "transportinstruction.pb.h" 38 : #include "compressor.h" 39 : #include "fatal_assert.h" 40 : 41 : using namespace Network; 42 : using namespace TransportBuffers; 43 : 44 5333 : static string network_order_string( uint16_t host_order ) 45 : { 46 5333 : uint16_t net_int = htobe16( host_order ); 47 5333 : return string( (char *)&net_int, sizeof( net_int ) ); 48 : } 49 : 50 5333 : static string network_order_string( uint64_t host_order ) 51 : { 52 5333 : uint64_t net_int = htobe64( host_order ); 53 5333 : return string( (char *)&net_int, sizeof( net_int ) ); 54 : } 55 : 56 5333 : string Fragment::tostring( void ) 57 : { 58 5333 : assert( initialized ); 59 : 60 5333 : string ret; 61 : 62 10666 : ret += network_order_string( id ); 63 : 64 5333 : fatal_assert( !( fragment_num & 0x8000 ) ); /* effective limit on size of a terminal screen change or buffered user input */ 65 5333 : uint16_t combined_fragment_num = ( final << 15 ) | fragment_num; 66 10666 : ret += network_order_string( combined_fragment_num ); 67 : 68 5333 : assert( ret.size() == frag_header_len ); 69 : 70 5333 : ret += contents; 71 : 72 5333 : return ret; 73 0 : } 74 : 75 5285 : Fragment::Fragment( const string &x ) 76 5285 : : id( -1 ), fragment_num( -1 ), final( false ), initialized( true ), 77 5285 : contents() 78 : { 79 5285 : fatal_assert( x.size() >= frag_header_len ); 80 5285 : contents = string( x.begin() + frag_header_len, x.end() ); 81 : 82 5285 : uint64_t data64; 83 5285 : uint16_t *data16 = (uint16_t *)x.data(); 84 5285 : memcpy( &data64, x.data(), sizeof( data64 ) ); 85 5285 : id = be64toh( data64 ); 86 5285 : fragment_num = be16toh( data16[ 4 ] ); 87 5285 : final = ( fragment_num & 0x8000 ) >> 15; 88 5285 : fragment_num &= 0x7FFF; 89 5285 : } 90 : 91 5285 : bool FragmentAssembly::add_fragment( Fragment &frag ) 92 : { 93 : /* see if this is a totally new packet */ 94 5285 : if ( current_id != frag.id ) { 95 5281 : fragments.clear(); 96 5281 : fragments.resize( frag.fragment_num + 1 ); 97 5281 : fragments.at( frag.fragment_num ) = frag; 98 5281 : fragments_arrived = 1; 99 5281 : fragments_total = -1; /* unknown */ 100 5281 : current_id = frag.id; 101 : } else { /* not a new packet */ 102 : /* see if we already have this fragment */ 103 4 : if ( (fragments.size() > frag.fragment_num) 104 4 : && (fragments.at( frag.fragment_num ).initialized) ) { 105 : /* make sure new version is same as what we already have */ 106 0 : assert( fragments.at( frag.fragment_num ) == frag ); 107 : } else { 108 4 : if ( (int)fragments.size() < frag.fragment_num + 1 ) { 109 4 : fragments.resize( frag.fragment_num + 1 ); 110 : } 111 4 : fragments.at( frag.fragment_num ) = frag; 112 4 : fragments_arrived++; 113 : } 114 : } 115 : 116 5285 : if ( frag.final ) { 117 5281 : fragments_total = frag.fragment_num + 1; 118 5281 : assert( (int)fragments.size() <= fragments_total ); 119 5281 : fragments.resize( fragments_total ); 120 : } 121 : 122 5285 : if ( fragments_total != -1 ) { 123 5281 : assert( fragments_arrived <= fragments_total ); 124 : } 125 : 126 : /* see if we're done */ 127 5285 : return fragments_arrived == fragments_total; 128 : } 129 : 130 5281 : Instruction FragmentAssembly::get_assembly( void ) 131 : { 132 5281 : assert( fragments_arrived == fragments_total ); 133 : 134 5281 : string encoded; 135 : 136 10566 : for ( int i = 0; i < fragments_total; i++ ) { 137 5285 : assert( fragments.at( i ).initialized ); 138 10570 : encoded += fragments.at( i ).contents; 139 : } 140 : 141 5281 : Instruction ret; 142 5281 : fatal_assert( ret.ParseFromString( get_compressor().uncompress_str( encoded ) ) ); 143 : 144 5281 : fragments.clear(); 145 5281 : fragments_arrived = 0; 146 5281 : fragments_total = -1; 147 : 148 5281 : return ret; 149 5281 : } 150 : 151 0 : bool Fragment::operator==( const Fragment &x ) const 152 : { 153 0 : return ( id == x.id ) && ( fragment_num == x.fragment_num ) && ( final == x.final ) 154 0 : && ( initialized == x.initialized ) && ( contents == x.contents ); 155 : } 156 : 157 5329 : vector<Fragment> Fragmenter::make_fragments( const Instruction &inst, size_t MTU ) 158 : { 159 5329 : MTU -= Fragment::frag_header_len; 160 5329 : if ( (inst.old_num() != last_instruction.old_num()) 161 1974 : || (inst.new_num() != last_instruction.new_num()) 162 202 : || (inst.ack_num() != last_instruction.ack_num()) 163 202 : || (inst.throwaway_num() != last_instruction.throwaway_num()) 164 202 : || (inst.chaff() != last_instruction.chaff()) 165 0 : || (inst.protocol_version() != last_instruction.protocol_version()) 166 5329 : || (last_MTU != MTU) ) { 167 5329 : next_instruction_id++; 168 : } 169 : 170 5329 : if ( (inst.old_num() == last_instruction.old_num()) 171 5329 : && (inst.new_num() == last_instruction.new_num()) ) { 172 202 : assert( inst.diff() == last_instruction.diff() ); 173 : } 174 : 175 5329 : last_instruction = inst; 176 5329 : last_MTU = MTU; 177 : 178 5329 : string payload = get_compressor().compress_str( inst.SerializeAsString() ); 179 5329 : uint16_t fragment_num = 0; 180 5329 : vector<Fragment> ret; 181 : 182 10662 : while ( !payload.empty() ) { 183 5333 : string this_fragment; 184 5333 : bool final = false; 185 : 186 5333 : if ( payload.size() > MTU ) { 187 4 : this_fragment = string( payload.begin(), payload.begin() + MTU ); 188 8 : payload = string( payload.begin() + MTU, payload.end() ); 189 : } else { 190 5329 : this_fragment = payload; 191 5329 : payload.clear(); 192 5329 : final = true; 193 : } 194 : 195 10666 : ret.push_back( Fragment( next_instruction_id, fragment_num++, final, this_fragment ) ); 196 5333 : } 197 : 198 5329 : return ret; 199 5329 : }