m_ipmodem.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       m_ipmodem.cc
00003 ///             Mode class for GPRS modem mode (using endpoints on
00004 ///             modern devices)
00005 ///
00006 
00007 /*
00008     Copyright (C) 2008-2009, Net Direct Inc. (http://www.netdirect.ca/)
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
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 }; // 0x12345678
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 // Mode::IpModem class
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         // Build the password hash
00082         // first, hash the password by itself
00083         SHA1((unsigned char *) password, strlen(password), pwdigest);
00084 
00085         // prefix the resulting hash with the provided seed
00086         memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
00087         memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
00088 
00089         // hash again
00090         SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
00091 
00092         // Build the response packet
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         // Send the password response packet
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         // Added for the BB Storm 9000's second password request
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                         // do nothing on timeouts
00111                         ddout("IPModem: Null Response Timeout");
00112                 }
00113         }
00114 
00115         //
00116         // check response 04 00 00 00 .......
00117         // On the 8703e the seed is incremented, retries are reset to 10
00118         // when the password is accepted.
00119         //
00120         // If data.GetData() + 4 is = to the orginal seed +1 or 00 00 00 00
00121         // then the password was acceppted.
00122         //
00123         // When data.GetData() + 4 is not 00 00 00 00 then data.GetData()[8]
00124         // contains the number of password retrys left.
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                         // Create session key - last 8 bytes of the password hash
00136                         memcpy(&m_session_key[0],
00137                                 pwdigest + SHA_DIGEST_LENGTH - sizeof(m_session_key),
00138                                 sizeof(m_session_key));
00139 
00140                         // blank password hashes as we don't need these anymore
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         // Unknown packet
00151         ddout("IPModem: Error unknown packet.\n" << data);
00152         return false;
00153 }
00154 
00155 //////////////////////////////////////////////////////////////////////////////
00156 // protected API / static functions
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                         // is it a special code?
00172                         if( data.GetSize() > 4 &&
00173                             memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag)) == 0 ) {
00174                                 // log, then drop it on the floor for now
00175                                 ddout("IPModem: Special packet:\n" << data);
00176                                 continue;
00177                         }
00178 
00179                         // call callback if available
00180                         if( ipmodem->m_callback ) {
00181                                 (*ipmodem->m_callback)(ipmodem->m_callback_context,
00182                                         data.GetData(),
00183                                         data.GetSize());
00184                         }
00185 //                      else {
00186 //                              // append data to readCache
00187 //                              FIXME;
00188 //                      }
00189 
00190                 }
00191                 catch( Usb::Timeout &to ) {
00192                         // do nothing on timeouts
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 // public API
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         // check that we have endpoints for the modem
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         // clear halt when starting out
00227         m_dev.ClearHalt(pair.read);
00228         m_dev.ClearHalt(pair.write);
00229 
00230         // Send stop command
00231         ddout("IPModem: Sending Stop Response:\n");
00232         m_dev.BulkWrite(write_ep, stop, sizeof(stop));
00233         try {
00234                 m_dev.BulkRead(read_ep, data, 500);
00235                 ddout("IPModem: Stop Response Packet:\n" << data);
00236         }
00237         catch( Usb::Timeout &to ) {
00238                 // do nothing on timeouts
00239                 ddout("IPModem: Stop Response Timeout");
00240         }
00241 
00242         // Send start commands to figure out if the device needs a password.
00243         ddout("IPModem: Sending Start Response:\n");
00244         m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00245         m_dev.BulkRead(read_ep, data, 5000);
00246         ddout("IPModem: Start Response Packet:\n" << data);
00247 
00248         // check for 02 00 00 00 SS SS SS SS RR 00 00 00 0a 00 00 00 PP PP PP PP PP 00 00 00 78 56 34 12
00249         if( data.GetSize() >= 9 && data.GetData()[0] == 0x02  &&
00250             memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
00251                 // Got a password request packet
00252                 ddout("IPModem: Password request packet:\n" << data);
00253 
00254                 // Check how many retries are left
00255                 if( data.GetData()[8] < BARRY_MIN_PASSWORD_TRIES ) {
00256                         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.",
00257                                 data.GetData()[8],
00258                                 true);
00259                 }
00260                 memcpy(&seed, data.GetData() + 4, sizeof(seed));
00261                 // Send password
00262                 if( !SendPassword(password, seed) ) {
00263                         throw Barry::Error("IpModem: Error sending password.");
00264                 }
00265 
00266                 // Re-send "start" packet
00267                 ddout("IPModem: Re-sending Start Response:\n");
00268                 m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00269                 m_dev.BulkRead(read_ep, data);
00270                 ddout("IPModem: Start Response Packet:\n" << data);
00271         }
00272 
00273         // send packet with the session_key
00274         unsigned char response_header[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
00275         memcpy(&response[0], response_header, sizeof(response_header));
00276         memcpy(&response[16], m_session_key,  sizeof(m_session_key));
00277         memcpy(&response[24], special_flag, sizeof(special_flag));
00278         ddout("IPModem: Sending Session key:\n");
00279         m_dev.BulkWrite(write_ep, response, sizeof(response));
00280         if( data.GetSize() >= 16 ) {
00281                 switch(data.GetData()[0])
00282                 {
00283                 case 0x00:      // Null packet
00284                         break;
00285 
00286                 case 0x02:      // password seed received
00287                         memcpy(&seed, data.GetData() + 4, sizeof(uint32_t));
00288                         if( !SendPassword( password, seed ) ) {
00289                                 throw Barry::Error("IpModem: Error sending password.");
00290                         }
00291                         break;
00292                 case 0x04:      // command accepted
00293                         break;
00294 
00295                 default:        // ???
00296                         ddout("IPModem: Unknown response.\n");
00297                         break;
00298                 }
00299         }
00300 
00301         // see if the modem will respond to commands
00302         const char modem_command[] = { "AT\r" };
00303         m_dev.BulkWrite(write_ep, modem_command, strlen(modem_command));
00304         m_dev.BulkRead(read_ep, data);
00305         ddout("IPModem: Test command response.\n" << data);
00306         if( data.GetSize() >= 1 ) {
00307                 switch(data.GetData()[0])
00308                 {
00309                 case 0x00:      // Null packet
00310                         try {
00311                                 m_dev.BulkRead(read_ep, data, 5000);
00312                                 ddout("IPModem: AT Response Packet:\n" << data);
00313                         }
00314                         catch( Usb::Timeout &to ) {
00315                                 // do nothing on timeouts
00316                                 ddout("IPModem: AT Response Timeout");
00317                         }
00318                         break;
00319 
00320                 case 0x02:      // password seed received
00321                         if( !password || strlen(password) == 0 ) {
00322                                 throw BadPassword("This device requested a password.",
00323                                         data.GetSize() >= 9 ? data.GetData()[8] : 0, false);
00324                         }
00325                         else {  // added for the Storm 9000
00326                                 memcpy(&seed, data.GetData() + 4, sizeof(seed));
00327                                 if( !SendPassword( password, seed ) ) {
00328                                         throw Barry::Error("IpModem: Error sending password.");
00329                                 }
00330                         }
00331                         break;
00332                 case 0x04:      // command accepted
00333                         break;
00334 
00335                 case 0x07:      // device is password protected?
00336                         throw BadPassword("This device requires a password.", 0, false);
00337 
00338                 default:        // ???
00339                         ddout("IPModem: Unknown AT command response.\n");
00340                         break;
00341                 }
00342         }
00343         ddout("IPModem: Modem Ready.\n");
00344 
00345         // spawn read thread
00346         m_continue_reading = true;
00347         int ret = pthread_create(&m_modem_read_thread, NULL, &IpModem::DataReadThread, this);
00348         if( ret ) {
00349                 m_continue_reading = false;
00350                 throw Barry::ErrnoError("IpModem: Error creating USB read thread.", ret);
00351         }
00352 }
00353 
00354 void IpModem::Write(const Data &data, int timeout)
00355 {
00356         if( data.GetSize() == 0 )
00357                 return; // nothing to do
00358 
00359         // according to Rick Scott the m_filter is not needed with the ip modem
00360         // but with the 8320 with Rogers, it doesn't seem to connect without it
00361         // If this is a performance problem, perhaps make this a runtime
00362         // option.
00363         m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
00364                 m_filter.Write(data), timeout);
00365 }
00366 
00367 void IpModem::Close()
00368 {
00369         // This is the terminate connection sequence
00370         // that resets the modem so we can re-connect
00371         // without unpluging the USB cable or reseting
00372         // the whole device.
00373         // This works on a BB 8703e a with password. other BB's??
00374         unsigned char end[28];
00375         int read_ep  = m_con.GetProbeResult().m_epModem.read;
00376         int write_ep = m_con.GetProbeResult().m_epModem.write;
00377         Data data;
00378 
00379         //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
00380         ddout("IpModem: Closing connection.");
00381         memset(end, 0, sizeof(end));
00382         end[4]  = 0xb0;
00383         end[13] = 0xc2;
00384         end[14] = 0x01;
00385         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00386         memcpy(&end[24], special_flag, sizeof(special_flag));
00387         m_dev.BulkWrite(write_ep, end, sizeof(end));
00388 
00389         //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
00390         memset(end, 0, sizeof(end));
00391         end[4]  = 0x20;
00392         end[8]  = 0x03;
00393         end[13] = 0xc2;
00394         end[14] = 0x01;
00395         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00396         memcpy(&end[24], special_flag, sizeof(special_flag));
00397         m_dev.BulkWrite(write_ep, end, sizeof(end));
00398 
00399         //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
00400         // The session_key is set to 0x0's when there is no password.
00401         memset(end, 0, sizeof(end));
00402         end[4]  = 0x30;
00403         end[13] = 0xc2;
00404         end[14] = 0x01;
00405         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00406         memcpy(&end[24], special_flag, sizeof(special_flag));
00407         m_dev.BulkWrite(write_ep, end, sizeof(end));
00408         m_dev.BulkWrite(write_ep, stop, sizeof(stop));
00409         try {
00410                 m_dev.BulkRead(read_ep, data, 5000);
00411                 ddout("IPModem: Close read packet:\n" << data);
00412         }
00413         catch( Usb::Timeout &to ) {
00414                 // do nothing on timeouts
00415                 ddout("IPModem: Close Read Timeout");
00416         }
00417         // stop the read thread
00418         if( m_continue_reading ) {
00419                 m_continue_reading = false;
00420                 pthread_join(m_modem_read_thread, NULL);
00421         }
00422         ddout("IPmodem: Closed!");
00423 
00424 }
00425 
00426 }} // namespace Barry::Mode
00427 

Generated on Tue Jun 30 16:08:13 2009 for Barry by  doxygen 1.5.8