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-2011, 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 only if needed
00227         if( m_con.m_result.m_needClearHalt ) {
00228                 m_dev.ClearHalt(pair.read);
00229                 m_dev.ClearHalt(pair.write);
00230         }
00231 
00232         // Send stop command
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                 // do nothing on timeouts
00241                 ddout("IPModem: Stop Response Timeout");
00242         }
00243 
00244         // Send start commands to figure out if the device needs a password.
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         // 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
00251         if( data.GetSize() >= 9 && data.GetData()[0] == 0x02  &&
00252             memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
00253                 // Got a password request packet
00254                 ddout("IPModem: Password request packet:\n" << data);
00255 
00256                 // Check how many retries are left
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                 // Send password
00264                 if( !SendPassword(password, seed) ) {
00265                         throw Barry::Error("IpModem: Error sending password.");
00266                 }
00267 
00268                 // Re-send "start" packet
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         // send packet with the session_key
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:      // Null packet
00286                         break;
00287 
00288                 case 0x02:      // password seed received
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:      // command accepted
00295                         break;
00296 
00297                 default:        // ???
00298                         ddout("IPModem: Unknown response.\n");
00299                         break;
00300                 }
00301         }
00302 
00303         // see if the modem will respond to commands
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:      // Null packet
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                                 // do nothing on timeouts
00318                                 ddout("IPModem: AT Response Timeout");
00319                         }
00320                         break;
00321 
00322                 case 0x02:      // password seed received
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 {  // added for the Storm 9000
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:      // command accepted
00335                         break;
00336 
00337                 case 0x07:      // device is password protected?
00338                         throw BadPassword("This device requires a password.", 0, false);
00339 
00340                 default:        // ???
00341                         ddout("IPModem: Unknown AT command response.\n");
00342                         // treat this unknown data as a serial response
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         // spawn read thread
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; // nothing to do
00366 
00367         // according to Rick Scott the m_filter is not needed with the ip modem
00368         // but with the 8320 with Rogers, it doesn't seem to connect without it
00369         // If this is a performance problem, perhaps make this a runtime
00370         // option.
00371         m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
00372                 m_filter.Write(data), timeout);
00373 }
00374 
00375 void IpModem::Close()
00376 {
00377         // This is the terminate connection sequence
00378         // that resets the modem so we can re-connect
00379         // without unpluging the USB cable or reseting
00380         // the whole device.
00381         // This works on a BB 8703e a with password. other BB's??
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         //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
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         //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
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         //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
00408         // The session_key is set to 0x0's when there is no password.
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                 // do nothing on timeouts
00423                 ddout("IPModem: Close Read Timeout");
00424         }
00425         // stop the read thread
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 }} // namespace Barry::Mode
00435 

Generated on Tue Mar 1 17:50:15 2011 for Barry by  doxygen 1.5.6