3 /// Mode class for GPRS modem mode (using endpoints on
8 Copyright (C) 2008, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "m_ipmodem.h"
24 #include "controller.h"
31 namespace Barry
{ namespace Mode
{
33 const char special_flag
[] = { 0x78, 0x56, 0x34, 0x12 }; // 0x12345678
34 const char start
[] = { 0x01, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
35 const char pw_start
[] = { 0x01, 0, 0, 0, 1, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
36 const char stop
[] = { 0x01, 0, 0, 0, 0, 0, 0, 0, 0x78, 0x56, 0x34, 0x12 };
38 //////////////////////////////////////////////////////////////////////////////
39 // Mode::IpModem class
41 IpModem::IpModem(Controller
&con
,
42 DeviceDataCallback callback
,
43 void *callback_context
)
46 , m_continue_reading(false)
47 , m_callback(callback
)
48 , m_callback_context(callback_context
)
50 memset(m_session_key
, 0, sizeof(m_session_key
));
57 } catch( std::exception
&e
) {
58 dout("Exception caught in IpModem destructor, ignoring: "
63 bool IpModem::SendPassword(const char *password
)
65 if( !password
|| strlen(password
) == 0 ) {
66 throw BadPassword("No password provided.", 0, false);
69 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
70 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
73 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
74 m_dev
.BulkRead(read_ep
, data
);
75 ddout("IPModem read packet.\n" << data
);
77 // Need to add checks for other packet types.
78 // 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
79 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x02 &&
80 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
))== 0 ) {
81 // Got a password request packet
82 ddout("IPModem password request packet:\n" << data
);
84 // Check how many retries are left
85 if( data
.GetData()[8] < BARRY_MIN_PASSWORD_TRIES
) {
86 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.",
91 // Build the password hash
92 unsigned char pwdigest
[SHA_DIGEST_LENGTH
];
93 unsigned char prefixedhash
[SHA_DIGEST_LENGTH
+ 4];
94 unsigned char pw_response
[SHA_DIGEST_LENGTH
+ 8];
95 unsigned char seed
[4];
97 // first, hash the password by itself
98 SHA1((unsigned char *) password
, strlen(password
), pwdigest
);
100 // prefix the resulting hash with the provided seed
101 memcpy(&seed
[0], data
.GetData() + 4, sizeof(uint32_t));
102 memcpy(&prefixedhash
[0], &seed
, sizeof(uint32_t));
103 memcpy(&prefixedhash
[4], pwdigest
, SHA_DIGEST_LENGTH
);
106 SHA1((unsigned char *) prefixedhash
, SHA_DIGEST_LENGTH
+ 4, pwdigest
);
108 // Build the response packet
109 const char pw_rsphdr
[] = { 0x03, 0x00, 0x00, 0x00 };
110 memcpy(&pw_response
[0], pw_rsphdr
, sizeof(pw_rsphdr
));
111 memcpy(&pw_response
[4], pwdigest
, SHA_DIGEST_LENGTH
);
112 memcpy(&pw_response
[24], special_flag
, sizeof(special_flag
));
114 // Send the password response packet
115 m_dev
.BulkWrite(write_ep
, pw_response
, sizeof(pw_response
));
116 m_dev
.BulkRead(read_ep
, data
);
117 ddout("IPModem read password response.\n" << data
);
119 // check response 04 00 00 00 .......
120 // On the 8703e the seed is incremented, retries are reset to 10 when the password is accepted.
121 // if( data.GetData()[0] == 0x04 && data.GetData()[8] == 0x0a ) {
122 if( data
.GetSize() >= 9 && data
.GetData()[0] == 0x04 ) {
123 if( memcmp(data
.GetData() + 4, seed
, sizeof(seed
)) == 0 ) {
124 ddout("IPModem invalid password.\n" << data
);
125 throw BadPassword("Password rejected by device.", data
.GetData()[8], false);
127 ddout("IPModem password accepted.\n");
128 // send "start"? packet
129 m_dev
.BulkWrite(write_ep
, pw_start
, sizeof(pw_start
));
131 // send packet with the last 8 bytes of the password hash (session_key?)
132 //unsigned char pw_response[SHA_DIGEST_LENGTH + 8];
133 unsigned char pw_rsphdr
[] = { 0x00, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0xc2, 1, 0 };
134 memcpy(&m_session_key
[0], pwdigest
+ 12, sizeof(m_session_key
));
135 memcpy(&pw_response
[0], pw_rsphdr
, sizeof(pw_rsphdr
));
136 memcpy(&pw_response
[16], pwdigest
+ 12, 8);
137 //memcpy(&pw_response[16], m_session_key, 8);
138 memcpy(&pw_response
[24], special_flag
, sizeof(special_flag
));
139 m_dev
.BulkWrite(write_ep
, pw_response
, sizeof(pw_response
));
141 // blank password hashes as we don't need these anymore
142 memset(pwdigest
, 0, sizeof(pwdigest
));
143 memset(prefixedhash
, 0, sizeof(prefixedhash
));
145 // The modem should be ready to accept AT commands
150 ddout("IPModem Error unknown packet.\n" << data
);
155 //////////////////////////////////////////////////////////////////////////////
156 // protected API / static functions
158 void *IpModem::DataReadThread(void *userptr
)
160 IpModem
*ipmodem
= (IpModem
*) userptr
;
162 int read_ep
= ipmodem
->m_con
.GetProbeResult().m_epModem
.read
;
165 while( ipmodem
->m_continue_reading
) {
169 ipmodem
->m_dev
.BulkRead(read_ep
, data
, 5000);
171 // is it a special code?
172 if( data
.GetSize() > 4 &&
173 memcmp(data
.GetData() + data
.GetSize() - 4, special_flag
, sizeof(special_flag
)) == 0 ) {
174 // log, then drop it on the floor for now
175 ddout("IPModem special packet:\n" << data
);
179 // call callback if available
180 if( ipmodem
->m_callback
) {
181 (*ipmodem
->m_callback
)(ipmodem
->m_callback_context
,
186 // // append data to readCache
191 catch( Usb::Timeout
&to
) {
192 // do nothing on timeouts
193 ddout("Timeout in DataReadThread!");
195 catch( std::exception
&e
) {
196 eout("Exception in IpModem::DataReadThread: " << e
.what());
203 //////////////////////////////////////////////////////////////////////////////
206 void IpModem::Open(const char *password
)
208 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
209 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
211 // check that we have endpoints for the modem
212 const Usb::EndpointPair
&pair
= m_con
.GetProbeResult().m_epModem
;
213 if( !pair
.IsComplete() ) {
214 std::ostringstream oss
;
215 oss
<< "IP Modem not supported by this device: "
216 << "read: " << std::hex
<< (unsigned int) pair
.read
217 << " write: " << std::hex
<< (unsigned int) pair
.write
218 << " type: " << std::hex
<< (unsigned int) pair
.type
;
220 throw Barry::Error(oss
.str());
223 // clear halt when starting out
224 m_dev
.ClearHalt(pair
.read
);
225 m_dev
.ClearHalt(pair
.write
);
227 if( !password
|| strlen(password
) == 0 ) {
228 Data
block(start
, sizeof(start
));
232 if( !SendPassword(password
) ) {
233 throw Barry::Error("IpModem:: Error sending password.");
237 // see if the modem will respond to commands
238 const char modem_command
[] = { "AT\r" };
239 m_dev
.BulkWrite(write_ep
, modem_command
, sizeof(modem_command
));
240 m_dev
.BulkRead(read_ep
, data
);
241 ddout("IPModem:: AT command response.\n" << data
);
242 if( data
.GetSize() >= 1 ) {
243 switch(data
.GetData()[0])
245 case 0x02: // password seed received
246 throw BadPassword("This device requested a password.",
247 data
.GetSize() >= 9 ? data
.GetData()[8] : 0, false);
249 case 0x04: // command accepted
252 case 0x07: // device is password protected?
253 throw BadPassword("This device requires a password.", 0, false);
256 ddout("IPModem:: unknown AT command response.\n");
262 m_continue_reading
= true;
263 int ret
= pthread_create(&m_modem_read_thread
, NULL
, &IpModem::DataReadThread
, this);
265 m_continue_reading
= false;
266 throw Barry::ErrnoError("IpModem:: Error creating USB read thread.", ret
);
270 void IpModem::Write(const Data
&data
, int timeout
)
272 if( data
.GetSize() == 0 )
273 return; // nothing to do
275 // according to Rick Scott the m_filter is not needed with the ip modem
276 // but with the 8320 with Rogers, it doesn't seem to connect without it
277 // If this is a performance problem, perhaps make this a runtime
279 m_dev
.BulkWrite(m_con
.GetProbeResult().m_epModem
.write
,
280 m_filter
.Write(data
), timeout
);
283 void IpModem::Close()
285 // This is the terminate connection sequence
286 // that resets the modem so we can re-connect
287 // without unpluging the USB cable or reseting
289 // This works on a BB 8703e a with password. other BB's??
290 unsigned char end
[28];
291 int read_ep
= m_con
.GetProbeResult().m_epModem
.read
;
292 int write_ep
= m_con
.GetProbeResult().m_epModem
.write
;
295 //0 0 0 0 b0 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
296 ddout("IpModem:: Closing connection.");
297 memset(end
, 0, sizeof(end
));
301 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
302 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
303 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
305 //0 0 0 0 20 0 0 0 3 0 0 0 0 c2 1 0 + session_key + special_flag
306 memset(end
, 0, sizeof(end
));
311 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
312 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
313 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
315 //0 0 0 0 30 0 0 0 0 0 0 0 0 c2 1 0 + session_key + special_flag
316 // The session_key is set to 0x0's when there is no password.
317 memset(end
, 0, sizeof(end
));
321 memcpy(&end
[16], m_session_key
, sizeof(m_session_key
));
322 memcpy(&end
[24], special_flag
, sizeof(special_flag
));
323 m_dev
.BulkWrite(write_ep
, end
, sizeof(end
));
324 m_dev
.BulkWrite(write_ep
, stop
, sizeof(stop
));
326 m_dev
.BulkRead(read_ep
, data
, 5000);
327 ddout("IPModem:: Close read packet:\n" << data
);
329 catch( Usb::Timeout
&to
) {
330 // do nothing on timeouts
331 ddout("IPModem:: Close Read Timeout");
333 // stop the read thread
334 if( m_continue_reading
) {
335 m_continue_reading
= false;
336 pthread_join(m_modem_read_thread
, NULL
);
338 ddout("IPmodem:: Closed!");
342 }} // namespace Barry::Mode