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, 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)
00064 {
00065         if( !password || strlen(password) == 0  ) {
00066                 throw BadPassword("No password provided.", 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         Data data;
00072 
00073         m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00074         m_dev.BulkRead(read_ep, data);
00075         ddout("IPModem read packet.\n" << data);
00076 
00077         // Need to add checks for other packet types.
00078         // 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
00079         if( data.GetSize() >= 9 && data.GetData()[0] == 0x02  &&
00080             memcmp(data.GetData() + data.GetSize() - 4, special_flag, sizeof(special_flag))== 0 ) {
00081                 // Got a password request packet
00082                 ddout("IPModem password request packet:\n" << data);
00083 
00084                 // Check how many retries are left
00085                 if( data.GetData()[8] < BARRY_MIN_PASSWORD_TRIES ) {
00086                         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.",
00087                                 data.GetData()[8],
00088                                 true);
00089                 }
00090 
00091                 // Build the password hash
00092                 unsigned char pwdigest[SHA_DIGEST_LENGTH];
00093                 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
00094                 unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
00095                 unsigned char seed[4];
00096 
00097                 // first, hash the password by itself
00098                 SHA1((unsigned char *) password, strlen(password), pwdigest);
00099 
00100                 // prefix the resulting hash with the provided seed
00101                 memcpy(&seed[0], data.GetData() + 4, sizeof(uint32_t));
00102                 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
00103                 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
00104 
00105                 // hash again
00106                 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
00107 
00108                 // Build the response packet
00109                 const char pw_rsphdr[]  = { 0x03, 0x00, 0x00, 0x00 };
00110                 memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
00111                 memcpy(&pw_response[4], pwdigest, SHA_DIGEST_LENGTH);
00112                 memcpy(&pw_response[24], special_flag, sizeof(special_flag));
00113 
00114                 // Send the password response packet
00115                 m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response));
00116                 m_dev.BulkRead(read_ep, data);
00117                 ddout("IPModem read password response.\n" << data);
00118 
00119                 // check response 04 00 00 00 .......
00120                 // On the 8703e the seed is incremented, retries are reset to 10 when the password is accepted.
00121                 // if( data.GetData()[0] == 0x04  && data.GetData()[8] == 0x0a ) {
00122                 if( data.GetSize() >= 9 && data.GetData()[0] == 0x04 ) {
00123                         if( memcmp(data.GetData() + 4, seed, sizeof(seed)) == 0 ) {
00124                                 ddout("IPModem invalid password.\n" << data);
00125                                 throw BadPassword("Password rejected by device.", data.GetData()[8], false);
00126                         }
00127                         ddout("IPModem password accepted.\n");
00128                         // send "start"? packet
00129                         m_dev.BulkWrite(write_ep, pw_start, sizeof(pw_start));
00130 
00131                         // send packet with the last 8 bytes of the password hash (session_key?)
00132                         //unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
00133                         unsigned char pw_rsphdr[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
00134                         memcpy(&m_session_key[0], pwdigest + 12, sizeof(m_session_key));
00135                         memcpy(&pw_response[0], pw_rsphdr, sizeof(pw_rsphdr));
00136                         memcpy(&pw_response[16], pwdigest + 12, 8);
00137                         //memcpy(&pw_response[16], m_session_key, 8);
00138                         memcpy(&pw_response[24], special_flag, sizeof(special_flag));
00139                         m_dev.BulkWrite(write_ep, pw_response, sizeof(pw_response));
00140 
00141                         // blank password hashes as we don't need these anymore
00142                         memset(pwdigest, 0, sizeof(pwdigest));
00143                         memset(prefixedhash, 0, sizeof(prefixedhash));
00144 
00145                         // The modem should be ready to accept AT commands
00146                         return true;
00147                 }
00148 
00149                 // Unknown packet
00150                 ddout("IPModem Error unknown packet.\n" << data);
00151         }
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("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         Data data;
00211         // check that we have endpoints for the modem
00212         const Usb::EndpointPair &pair = m_con.GetProbeResult().m_epModem;
00213         if( !pair.IsComplete() ) {
00214                 std::ostringstream oss;
00215                 oss << "IP Modem not supported by this device: "
00216                         << "read: " << std::hex << (unsigned int) pair.read
00217                         << " write: " << std::hex << (unsigned int) pair.write
00218                         << " type: " << std::hex << (unsigned int) pair.type;
00219                 eout(oss.str());
00220                 throw Barry::Error(oss.str());
00221         }
00222 
00223         // clear halt when starting out
00224         m_dev.ClearHalt(pair.read);
00225         m_dev.ClearHalt(pair.write);
00226 
00227         if( !password || strlen(password) == 0 ) {
00228                 Data block(start, sizeof(start));
00229                 Write(block);
00230         }
00231         else {
00232                 if( !SendPassword(password) ) {
00233                         throw Barry::Error("IpModem:: Error sending password.");
00234                 }
00235         }
00236 
00237         // see if the modem will respond to commands
00238         const char modem_command[] = { "AT\r" };
00239         m_dev.BulkWrite(write_ep, modem_command, strlen(modem_command));
00240         m_dev.BulkRead(read_ep, data);
00241         ddout("IPModem:: AT command response.\n" << data);
00242         if( data.GetSize() >= 1 ) {
00243                 switch(data.GetData()[0])
00244                 {
00245                 case 0x02:      // password seed received
00246                         throw BadPassword("This device requested a password.",
00247                                 data.GetSize() >= 9 ? data.GetData()[8] : 0, false);
00248 
00249                 case 0x04:      // command accepted
00250                         break;
00251 
00252                 case 0x07:      // device is password protected?
00253                         throw BadPassword("This device requires a password.", 0, false);
00254 
00255                 default:        // ???
00256                         ddout("IPModem:: unknown AT command response.\n");
00257                         break;
00258                 }
00259         }
00260 
00261         // spawn read thread
00262         m_continue_reading = true;
00263         int ret = pthread_create(&m_modem_read_thread, NULL, &IpModem::DataReadThread, this);
00264         if( ret ) {
00265                 m_continue_reading = false;
00266                 throw Barry::ErrnoError("IpModem:: Error creating USB read thread.", ret);
00267         }
00268 }
00269 
00270 void IpModem::Write(const Data &data, int timeout)
00271 {
00272         if( data.GetSize() == 0 )
00273                 return; // nothing to do
00274 
00275         // according to Rick Scott the m_filter is not needed with the ip modem
00276         // but with the 8320 with Rogers, it doesn't seem to connect without it
00277         // If this is a performance problem, perhaps make this a runtime
00278         // option.
00279         m_dev.BulkWrite(m_con.GetProbeResult().m_epModem.write,
00280                 m_filter.Write(data), timeout);
00281 }
00282 
00283 void IpModem::Close()
00284 {
00285         // This is the terminate connection sequence
00286         // that resets the modem so we can re-connect
00287         // without unpluging the USB cable or reseting
00288         // the whole device.
00289         // This works on a BB 8703e a with password. other BB's??
00290         unsigned char end[28];
00291         int read_ep  = m_con.GetProbeResult().m_epModem.read;
00292         int write_ep = m_con.GetProbeResult().m_epModem.write;
00293         Data data;
00294 
00295         //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
00296         ddout("IpModem:: Closing connection.");
00297         memset(end, 0, sizeof(end));
00298         end[4]  = 0xb0;
00299         end[13] = 0xc2;
00300         end[14] = 0x01;
00301         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00302         memcpy(&end[24], special_flag, sizeof(special_flag));
00303         m_dev.BulkWrite(write_ep, end, sizeof(end));
00304 
00305         //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
00306         memset(end, 0, sizeof(end));
00307         end[4]  = 0x20;
00308         end[8]  = 0x03;
00309         end[13] = 0xc2;
00310         end[14] = 0x01;
00311         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00312         memcpy(&end[24], special_flag, sizeof(special_flag));
00313         m_dev.BulkWrite(write_ep, end, sizeof(end));
00314 
00315         //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
00316         // The session_key is set to 0x0's when there is no password.
00317         memset(end, 0, sizeof(end));
00318         end[4]  = 0x30;
00319         end[13] = 0xc2;
00320         end[14] = 0x01;
00321         memcpy(&end[16], m_session_key,  sizeof(m_session_key));
00322         memcpy(&end[24], special_flag, sizeof(special_flag));
00323         m_dev.BulkWrite(write_ep, end, sizeof(end));
00324         m_dev.BulkWrite(write_ep, stop, sizeof(stop));
00325         try {
00326                 m_dev.BulkRead(read_ep, data, 5000);
00327                 ddout("IPModem:: Close read packet:\n" << data);
00328         }
00329         catch( Usb::Timeout &to ) {
00330                 // do nothing on timeouts
00331                 ddout("IPModem:: Close Read Timeout");
00332         }
00333         // stop the read thread
00334         if( m_continue_reading ) {
00335                 m_continue_reading = false;
00336                 pthread_join(m_modem_read_thread, NULL);
00337         }
00338         ddout("IPmodem:: Closed!");
00339 
00340 }
00341 
00342 }} // namespace Barry::Mode
00343 

Generated on Wed Sep 24 21:27:32 2008 for Barry by  doxygen 1.5.1