00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "m_ipmodem.h"
00024 #include "controller.h"
00025 #include "data.h"
00026 #include "debug.h"
00027 #include <sstream>
00028 #include <string.h>
00029 #include "sha1.h"
00030
00031 namespace Barry { namespace Mode {
00032
00033 const char special_flag[] = { 0x78, 0x56, 0x34, 0x12 };
00034 const char start[] = { 0x01, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
00035 const char pw_start[] = { 0x01, 0, 0, 0, 1, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
00036 const char stop[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
00037
00038
00039
00040
00041 IpModem::IpModem(Controller &con,
00042 DeviceDataCallback callback,
00043 void *callback_context)
00044 : m_con(con)
00045 , m_dev(con.m_dev)
00046 , m_continue_reading(false)
00047 , m_callback(callback)
00048 , m_callback_context(callback_context)
00049 {
00050 memset(m_session_key, 0, sizeof(m_session_key));
00051 }
00052
00053 IpModem::~IpModem()
00054 {
00055 try {
00056 Close();
00057 } catch( std::exception &e ) {
00058 dout("Exception caught in IpModem destructor, ignoring: "
00059 << e.what());
00060 }
00061 }
00062
00063 bool IpModem::SendPassword( const char *password, uint32_t seed )
00064 {
00065 if( !password || strlen(password) == 0 ) {
00066 throw BadPassword("Logic error: No password provided in SendPassword.", 0, false);
00067 }
00068
00069 int read_ep = m_con.GetProbeResult().m_epModem.read;
00070 int write_ep = m_con.GetProbeResult().m_epModem.write;
00071 unsigned char pwdigest[SHA_DIGEST_LENGTH];
00072 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
00073 unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
00074 uint32_t new_seed;
00075 Data data;
00076
00077 if( !password || strlen(password) == 0 ) {
00078 throw BadPassword("No password provided.", 0, false);
00079 }
00080
00081
00082
00083 SHA1((unsigned char *) password, strlen(password), pwdigest);
00084
00085
00086 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
00087 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
00088
00089
00090 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
00091
00092
00093 const char pw_rsphdr[] = { 0x03, 0x00, 0x00, 0x00 };
00094 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
00095 memcpy(&pw_response[4], pwdigest, SHA_DIGEST_LENGTH);
00096 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
00097
00098
00099 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response));
00100 m_dev.BulkRead(read_ep, data);
00101 ddout("IPModem: Read password response.\n" << data);
00102
00103
00104 if( data.GetSize() >= 16 && data.GetData()[0] == 0x00 ) {
00105 try {
00106 m_dev.BulkRead(read_ep, data, 500);
00107 ddout("IPModem: Null Response Packet:\n" << data);
00108 }
00109 catch( Usb::Timeout &to ) {
00110
00111 ddout("IPModem: Null Response Timeout");
00112 }
00113 }
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 if( data.GetSize() >= 9 && data.GetData()[0] == 0x04 ) {
00127 memcpy(&new_seed, data.GetData() + 4, sizeof(uint32_t));
00128 seed++;
00129 if( seed == new_seed || new_seed == 0 ) {
00130 ddout("IPModem: Password accepted.\n");
00131
00132 #if SHA_DIGEST_LENGTH < SB_IPMODEM_SESSION_KEY_LENGTH
00133 #error Session key field must be smaller than SHA digest
00134 #endif
00135
00136 memcpy(&m_session_key[0],
00137 pwdigest + SHA_DIGEST_LENGTH - sizeof(m_session_key),
00138 sizeof(m_session_key));
00139
00140
00141 memset(pwdigest, 0, sizeof(pwdigest));
00142 memset(prefixedhash, 0, sizeof(prefixedhash));
00143 return true;
00144 }
00145 else {
00146 ddout("IPModem: Invalid password.\n" << data);
00147 throw BadPassword("Password rejected by device.", data.GetData()[8], false);
00148 }
00149 }
00150
00151 ddout("IPModem: Error unknown packet.\n" << data);
00152 return false;
00153 }
00154
00155
00156
00157
00158 void *IpModem::DataReadThread(void *userptr)
00159 {
00160 IpModem *ipmodem = (IpModem*) userptr;
00161
00162 int read_ep = ipmodem->m_con.GetProbeResult().m_epModem.read;
00163 Data data;
00164
00165 while( ipmodem->m_continue_reading ) {
00166
00167 try {
00168
00169 ipmodem->m_dev.BulkRead(read_ep, data, 5000);
00170
00171
00172 if( data.GetSize() > 4 &&
00173 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag)) == 0 ) {
00174
00175 ddout("IPModem: Special packet:\n" << data);
00176 continue;
00177 }
00178
00179
00180 if( ipmodem->m_callback ) {
00181 (*ipmodem->m_callback)(ipmodem->m_callback_context,
00182 data.GetData(),
00183 data.GetSize());
00184 }
00185
00186
00187
00188
00189
00190 }
00191 catch( Usb::Timeout &to ) {
00192
00193 ddout("IPModem: Timeout in DataReadThread!");
00194 }
00195 catch( std::exception &e ) {
00196 eout("Exception in IpModem::DataReadThread: " << e.what());
00197 }
00198 }
00199
00200 return 0;
00201 }
00202
00203
00204
00205
00206 void IpModem::Open(const char *password)
00207 {
00208 int read_ep = m_con.GetProbeResult().m_epModem.read;
00209 int write_ep = m_con.GetProbeResult().m_epModem.write;
00210 unsigned char response[28];
00211 uint32_t seed;
00212 Data data;
00213
00214
00215 const Usb::EndpointPair &pair = m_con.GetProbeResult().m_epModem;
00216 if( !pair.IsComplete() ) {
00217 std::ostringstream oss;
00218 oss << "IP Modem not supported by this device: "
00219 << "read: " << std::hex << (unsigned int) pair.read
00220 << " write: " << std::hex << (unsigned int) pair.write
00221 << " type: " << std::hex << (unsigned int) pair.type;
00222 eout(oss.str());
00223 throw Barry::Error(oss.str());
00224 }
00225
00226
00227 if( m_con.m_result.m_needClearHalt ) {
00228 m_dev.ClearHalt(pair.read);
00229 m_dev.ClearHalt(pair.write);
00230 }
00231
00232
00233 ddout("IPModem: Sending Stop Response:\n");
00234 m_dev.BulkWrite(write_ep, stop, sizeof(stop));
00235 try {
00236 m_dev.BulkRead(read_ep, data, 500);
00237 ddout("IPModem: Stop Response Packet:\n" << data);
00238 }
00239 catch( Usb::Timeout &to ) {
00240
00241 ddout("IPModem: Stop Response Timeout");
00242 }
00243
00244
00245 ddout("IPModem: Sending Start Response:\n");
00246 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00247 m_dev.BulkRead(read_ep, data, 5000);
00248 ddout("IPModem: Start Response Packet:\n" << data);
00249
00250
00251 if( data.GetSize() >= 9 && data.GetData()[0] == 0x02 &&
00252 memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
00253
00254 ddout("IPModem: Password request packet:\n" << data);
00255
00256
00257 if( data.GetData()[8] < BARRY_MIN_PASSWORD_TRIES ) {
00258 throw BadPassword("Fewer than " BARRY_MIN_PASSWORD_TRIES_ASC " password tries remaining in device. Refusing to proceed, to avoid device zapping itself. Use a Windows client, or re-cradle the device.",
00259 data.GetData()[8],
00260 true);
00261 }
00262 memcpy(&seed, data.GetData() + 4, sizeof(seed));
00263
00264 if( !SendPassword(password, seed) ) {
00265 throw Barry::Error("IpModem: Error sending password.");
00266 }
00267
00268
00269 ddout("IPModem: Re-sending Start Response:\n");
00270 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00271 m_dev.BulkRead(read_ep, data);
00272 ddout("IPModem: Start Response Packet:\n" << data);
00273 }
00274
00275
00276 unsigned char response_header[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
00277 memcpy(&response[0], response_header, sizeof(response_header));
00278 memcpy(&response[16], m_session_key, sizeof(m_session_key));
00279 memcpy(&response[24], special_flag, sizeof(special_flag));
00280 ddout("IPModem: Sending Session key:\n");
00281 m_dev.BulkWrite(write_ep, response, sizeof(response));
00282 if( data.GetSize() >= 16 ) {
00283 switch(data.GetData()[0])
00284 {
00285 case 0x00:
00286 break;
00287
00288 case 0x02:
00289 memcpy(&seed, data.GetData() + 4, sizeof(uint32_t));
00290 if( !SendPassword( password, seed ) ) {
00291 throw Barry::Error("IpModem: Error sending password.");
00292 }
00293 break;
00294 case 0x04:
00295 break;
00296
00297 default:
00298 ddout("IPModem: Unknown response.\n");
00299 break;
00300 }
00301 }
00302
00303
00304 const char modem_command[] = { "AT\r" };
00305 m_dev.BulkWrite(write_ep, modem_command, strlen(modem_command));
00306 m_dev.BulkRead(read_ep, data);
00307 ddout("IPModem: Test command response.\n" << data);
00308 if( data.GetSize() >= 1 ) {
00309 switch(data.GetData()[0])
00310 {
00311 case 0x00:
00312 try {
00313 m_dev.BulkRead(read_ep, data, 5000);
00314 ddout("IPModem: AT Response Packet:\n" << data);
00315 }
00316 catch( Usb::Timeout &to ) {
00317
00318 ddout("IPModem: AT Response Timeout");
00319 }
00320 break;
00321
00322 case 0x02:
00323 if( !password || strlen(password) == 0 ) {
00324 throw BadPassword("This device requested a password.",
00325 data.GetSize() >= 9 ? data.GetData()[8] : 0, false);
00326 }
00327 else {
00328 memcpy(&seed, data.GetData() + 4, sizeof(seed));
00329 if( !SendPassword( password, seed ) ) {
00330 throw Barry::Error("IpModem: Error sending password.");
00331 }
00332 }
00333 break;
00334 case 0x04:
00335 break;
00336
00337 case 0x07:
00338 throw BadPassword("This device requires a password.", 0, false);
00339
00340 default:
00341 ddout("IPModem: Unknown AT command response.\n");
00342
00343 if( m_callback ) {
00344 (*m_callback)(m_callback_context,
00345 data.GetData(),
00346 data.GetSize());
00347 }
00348 break;
00349 }
00350 }
00351 ddout("IPModem: Modem Ready.\n");
00352
00353
00354 m_continue_reading = true;
00355 int ret = pthread_create(&m_modem_read_thread, NULL, &IpModem::DataReadThread, this);
00356 if( ret ) {
00357 m_continue_reading = false;
00358 throw Barry::ErrnoError("IpModem: Error creating USB read thread.", ret);
00359 }
00360 }
00361
00362 void IpModem::Write(const Data &data, int timeout)
00363 {
00364 if( data.GetSize() == 0 )
00365 return;
00366
00367
00368
00369
00370
00371 m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
00372 m_filter.Write(data), timeout);
00373 }
00374
00375 void IpModem::Close()
00376 {
00377
00378
00379
00380
00381
00382 unsigned char end[28];
00383 int read_ep = m_con.GetProbeResult().m_epModem.read;
00384 int write_ep = m_con.GetProbeResult().m_epModem.write;
00385 Data data;
00386
00387
00388 ddout("IpModem: Closing connection.");
00389 memset(end, 0, sizeof(end));
00390 end[4] = 0xb0;
00391 end[13] = 0xc2;
00392 end[14] = 0x01;
00393 memcpy(&end[16], m_session_key, sizeof(m_session_key));
00394 memcpy(&end[24], special_flag, sizeof(special_flag));
00395 m_dev.BulkWrite(write_ep, end, sizeof(end));
00396
00397
00398 memset(end, 0, sizeof(end));
00399 end[4] = 0x20;
00400 end[8] = 0x03;
00401 end[13] = 0xc2;
00402 end[14] = 0x01;
00403 memcpy(&end[16], m_session_key, sizeof(m_session_key));
00404 memcpy(&end[24], special_flag, sizeof(special_flag));
00405 m_dev.BulkWrite(write_ep, end, sizeof(end));
00406
00407
00408
00409 memset(end, 0, sizeof(end));
00410 end[4] = 0x30;
00411 end[13] = 0xc2;
00412 end[14] = 0x01;
00413 memcpy(&end[16], m_session_key, sizeof(m_session_key));
00414 memcpy(&end[24], special_flag, sizeof(special_flag));
00415 m_dev.BulkWrite(write_ep, end, sizeof(end));
00416 m_dev.BulkWrite(write_ep, stop, sizeof(stop));
00417 try {
00418 m_dev.BulkRead(read_ep, data, 5000);
00419 ddout("IPModem: Close read packet:\n" << data);
00420 }
00421 catch( Usb::Timeout &to ) {
00422
00423 ddout("IPModem: Close Read Timeout");
00424 }
00425
00426 if( m_continue_reading ) {
00427 m_continue_reading = false;
00428 pthread_join(m_modem_read_thread, NULL);
00429 }
00430 ddout("IPmodem: Closed!");
00431
00432 }
00433
00434 }}
00435