- more porting of OpenSync module to 0.30
[barry.git] / src / socket.cc
bloba181f5d0df8a4f57cf611c15cb6782ea5fc31ac7
1 ///
2 /// \file socket.cc
3 /// Class wrapper to encapsulate the Blackberry USB logical socket
4 ///
6 /*
7 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "socket.h"
23 #include "usbwrap.h"
24 #include "protocol.h"
25 #include "protostructs.h"
26 #include "debug.h"
27 #include "data.h"
28 #include "error.h"
29 #include "packet.h"
30 #include "endian.h"
31 #include <openssl/sha.h>
34 using namespace Usb;
37 namespace Barry {
39 Socket::Socket( Device &dev,
40 int writeEndpoint, int readEndpoint,
41 uint8_t zeroSocketSequenceStart)
42 : m_dev(dev),
43 m_writeEp(writeEndpoint),
44 m_readEp(readEndpoint),
45 m_socket(0),
46 m_zeroSocketSequence(zeroSocketSequenceStart),
47 m_flag(0),
48 m_sequenceId(0),
49 m_halfOpen(false),
50 m_challengeSeed(0),
51 m_remainingTries(0)
55 Socket::~Socket()
57 // trap exceptions in the destructor
58 try {
59 // a non-default socket has been opened, close it
60 Close();
62 catch( std::runtime_error &re ) {
63 // do nothing... log it?
64 dout("Exception caught in ~Socket: " << re.what());
68 void Socket::SendOpen(uint16_t socket, Data &receive)
70 // build open command
71 Barry::Protocol::Packet packet;
72 packet.socket = 0;
73 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
74 packet.command = SB_COMMAND_OPEN_SOCKET;
75 packet.u.socket.socket = htobs(socket);
76 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
78 // save sequence for later close, and inc
79 m_flag = m_zeroSocketSequence;
81 Data send(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
82 try {
83 Send(send, receive);
84 } catch( Usb::Error & ) {
85 eeout(send, receive);
86 throw;
89 // check sequence ID
90 Protocol::CheckSize(receive);
91 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
92 CheckSequence(receive);
94 // still need our ACK
95 Receive(receive);
98 // receive now holds the Open response
101 // SHA1 hashing logic based on Rick Scott's XmBlackBerry's send_password()
102 void Socket::SendPasswordHash(uint16_t socket, const char *password, Data &receive)
104 unsigned char pwdigest[SHA_DIGEST_LENGTH];
105 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
107 // first, hash the password by itself
108 SHA1((unsigned char *) password, strlen(password), pwdigest);
110 // prefix the resulting hash with the provided seed
111 uint32_t seed = htobl(m_challengeSeed);
112 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
113 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
115 // hash again
116 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
119 size_t size = SB_SOCKET_PACKET_HEADER_SIZE + PASSWORD_CHALLENGE_SIZE;
121 // build open command
122 Barry::Protocol::Packet packet;
123 packet.socket = 0;
124 packet.size = htobs(size);
125 packet.command = SB_COMMAND_PASSWORD;
126 packet.u.socket.socket = htobs(socket);
127 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
128 packet.u.socket.u.password.remaining_tries = 0;
129 packet.u.socket.u.password.unknown = 0;
130 packet.u.socket.u.password.param = htobs(0x14); // FIXME - what does this mean?
131 memcpy(packet.u.socket.u.password.u.hash, pwdigest,
132 sizeof(packet.u.socket.u.password.u.hash));
134 // blank password hashes as we don't need these anymore
135 memset(pwdigest, 0, sizeof(pwdigest));
136 memset(prefixedhash, 0, sizeof(prefixedhash));
138 // save sequence for later close, and inc
139 m_flag = m_zeroSocketSequence;
141 Data send(&packet, size);
142 Send(send, receive);
144 // blank password hash as we don't need this anymore either
145 memset(packet.u.socket.u.password.u.hash, 0,
146 sizeof(packet.u.socket.u.password.u.hash));
147 send.Zap();
149 // check sequence ID
150 Protocol::CheckSize(receive);
151 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
152 CheckSequence(receive);
154 // still need our ACK
155 Receive(receive);
158 // receive now holds the Password response
163 // Open
165 /// Open a logical socket on the device.
167 /// Both the socket number and the flag are based on the response to the
168 /// SELECT_MODE command. See Controller::SelectMode() for more info
169 /// on this.
171 /// The packet sequence is normal for most socket operations.
173 /// - Down: command packet with OPEN_SOCKET
174 /// - Up: optional sequence handshake packet
175 /// - Up: command response, which repeats the socket and flag data
176 /// as confirmation
178 /// \exception Barry::Error
179 /// Thrown on protocol error.
181 /// \exception Barry::BadPassword
182 /// Thrown on invalid password, or not enough retries left
183 /// on device.
185 void Socket::Open(uint16_t socket, const char *password)
187 if( m_socket != 0 ) {
188 // already open
189 throw Error("Socket: already open");
192 // Things get a little funky here, as we may be left in an
193 // intermediate state in the case of a failed password.
194 // This function should support being called as many times
195 // as needed to handle the password
197 Data send, receive;
198 ZeroPacket packet(send, receive);
200 if( !m_halfOpen ) {
201 // starting fresh
202 m_remainingTries = 0;
204 SendOpen(socket, receive);
206 // check for password challenge, or success
207 if( packet.Command() == SB_COMMAND_PASSWORD_CHALLENGE ) {
208 m_halfOpen = true;
209 m_challengeSeed = packet.ChallengeSeed();
210 m_remainingTries = packet.RemainingTries();
213 // fall through to challenge code...
216 if( m_halfOpen ) {
217 // half open, device is expecting a password hash... do we
218 // have a password?
219 if( !password ) {
220 throw BadPassword("No password specified.", m_remainingTries, false);
223 // only allow password attempts if there are 6 or more
224 // tries remaining... we want to give the user at least
225 // 5 chances on a Windows machine before the device
226 // commits suicide.
227 if( m_remainingTries < 6 ) {
228 throw BadPassword("Fewer than 6 password tries "
229 "remaining in device. Refusing to proceed, "
230 "to avoid device zapping itself. Use a "
231 "Windows client, or re-cradle the device.",
232 m_remainingTries,
233 true);
236 SendPasswordHash(socket, password, receive);
238 if( packet.Command() == SB_COMMAND_PASSWORD_FAILED ) {
239 m_halfOpen = true;
240 m_challengeSeed = packet.ChallengeSeed();
241 m_remainingTries = packet.RemainingTries();
242 throw BadPassword("Password rejected by device.", m_remainingTries, false);
245 // if we get this far, we are no longer in half-open password
246 // mode, so we can reset our flags
247 m_halfOpen = false;
249 // fall through to success check...
252 if( packet.Command() != SB_COMMAND_OPENED_SOCKET ||
253 packet.SocketResponse() != socket ||
254 packet.SocketSequence() != m_flag )
256 eout("Packet:\n" << receive);
257 throw Error("Socket: Bad OPENED packet in Open");
260 // success! save the socket
261 m_socket = socket;
265 // Close
267 /// Closes a non-default socket (i.e. non-zero socket number)
269 /// The packet sequence is just like Open(), except the command is
270 /// CLOSE_SOCKET.
272 /// \exception Barry::Error
274 void Socket::Close()
276 if( m_socket != 0 ) {
277 // only close non-default sockets
279 // build close command
280 Barry::Protocol::Packet packet;
281 packet.socket = 0;
282 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
283 packet.command = SB_COMMAND_CLOSE_SOCKET;
284 packet.u.socket.socket = htobs(m_socket);
285 packet.u.socket.sequence = m_flag;
287 Data command(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
288 Data response;
289 try {
290 Send(command, response);
292 catch( Usb::Error & ) {
293 // reset so this won't be called again
294 m_socket = 0;
295 m_flag = 0;
297 eeout(command, response);
298 throw;
301 // starting fresh, reset sequence ID
302 Protocol::CheckSize(response);
303 if( IS_COMMAND(response, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
304 CheckSequence(response);
306 // still need our ACK
307 Receive(response);
310 Protocol::CheckSize(response, SB_SOCKET_PACKET_HEADER_SIZE);
311 MAKE_PACKET(rpack, response);
312 if( rpack->command != SB_COMMAND_CLOSED_SOCKET ||
313 btohs(rpack->u.socket.socket) != m_socket ||
314 rpack->u.socket.sequence != m_flag )
316 // reset so this won't be called again
317 m_socket = 0;
318 m_flag = 0;
320 eout("Packet:\n" << response);
321 throw Error("Socket: Bad CLOSED packet in Close");
324 // and finally, there always seems to be an extra read of
325 // an empty packet at the end... just throw it away
326 // Receive(response);
328 // reset socket and flag
329 m_socket = 0;
330 m_flag = 0;
336 // Send
338 /// Sends 'send' data to device, no receive.
340 /// \returns void
342 /// \exception Usb::Error on underlying bus errors.
344 void Socket::Send(Data &send, int timeout)
346 // Special case: it seems that sending packets with a size that's an
347 // exact multiple of 0x40 causes the device to get confused.
349 // To get around that, it is observed in the captures that the size
350 // is sent in a special 3 byte packet before the real packet.
351 // Check for this case here.
353 if( (send.GetSize() % 0x40) == 0 ) {
354 Protocol::SizePacket packet;
355 packet.size = htobs(send.GetSize());
356 packet.buffer[2] = 0; // zero the top byte
357 Data sizeCommand(&packet, 3);
359 m_dev.BulkWrite(m_writeEp, sizeCommand);
362 // If this is a socket 0 packet, force the send packet data's
363 // socket 0 sequence number to something correct.
364 if( m_socket == 0 && send.GetSize() >= SB_SOCKET_PACKET_HEADER_SIZE ) {
365 MAKE_PACKETPTR_BUF(spack, send.GetBuffer());
366 spack->u.socket.sequence = m_zeroSocketSequence;
367 m_zeroSocketSequence++;
370 m_dev.BulkWrite(m_writeEp, send);
374 // Send
376 /// Sends 'send' data to device, and waits for response.
378 /// \returns void
380 /// \exception Usb::Error on underlying bus errors.
382 void Socket::Send(Data &send, Data &receive, int timeout)
384 Send(send, timeout);
385 m_dev.BulkRead(m_readEp, receive, timeout);
386 ddout("Socket::Send: Endpoint " << m_readEp << "\nReceived:\n" << receive);
389 void Socket::Send(Barry::Packet &packet, int timeout)
391 Send(packet.m_send, packet.m_receive, timeout);
394 void Socket::Receive(Data &receive, int timeout)
396 m_dev.BulkRead(m_readEp, receive, timeout);
398 ddout("Socket::Receive: Endpoint " << m_readEp << "\nReceived:\n" << receive);
401 // appends fragment to whole... if whole is empty, simply copies, and
402 // sets command to DATA instead of FRAGMENTED. Always updates the
403 // packet size of whole, to reflect the total size
404 void Socket::AppendFragment(Data &whole, const Data &fragment)
406 if( whole.GetSize() == 0 ) {
407 // empty, so just copy
408 whole = fragment;
410 else {
411 // has some data already, so just append
412 int size = whole.GetSize();
413 unsigned char *buf = whole.GetBuffer(size + fragment.GetSize());
414 MAKE_PACKET(fpack, fragment);
415 int fragsize = fragment.GetSize() - SB_FRAG_HEADER_SIZE;
417 memcpy(buf+size, &fpack->u.db.u.fragment, fragsize);
418 whole.ReleaseBuffer(size + fragsize);
421 // update whole's size and command type for future sanity
422 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) whole.GetBuffer();
423 wpack->size = htobs((uint16_t) whole.GetSize());
424 wpack->command = SB_COMMAND_DB_DATA;
425 // don't need to call ReleaseBuffer here, since we're not changing
426 // the real size size
429 // If offset is 0, starts fresh, taking the first fragment packet size chunk
430 // out of whole and creating a sendable packet in fragment. Returns the
431 // next offset if there is still more data, or 0 if finished.
432 unsigned int Socket::MakeNextFragment(const Data &whole, Data &fragment, unsigned int offset)
434 // sanity check
435 if( whole.GetSize() < SB_FRAG_HEADER_SIZE ) {
436 eout("Whole packet too short to fragment: " << whole.GetSize());
437 throw Error("Socket: Whole packet too short to fragment");
440 // calculate size
441 unsigned int todo = whole.GetSize() - SB_FRAG_HEADER_SIZE - offset;
442 unsigned int nextOffset = 0;
443 if( todo > (MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE) ) {
444 todo = MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE;
445 nextOffset = offset + todo;
448 // create fragment header
449 unsigned char *buf = fragment.GetBuffer(SB_FRAG_HEADER_SIZE + todo);
450 memcpy(buf, whole.GetData(), SB_FRAG_HEADER_SIZE);
452 // copy over a fragment size of data
453 memcpy(buf + SB_FRAG_HEADER_SIZE, whole.GetData() + SB_FRAG_HEADER_SIZE + offset, todo);
455 // update fragment's size and command type
456 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) buf;
457 wpack->size = htobs((uint16_t) (todo + SB_FRAG_HEADER_SIZE));
458 if( nextOffset )
459 wpack->command = SB_COMMAND_DB_FRAGMENTED;
460 else
461 wpack->command = SB_COMMAND_DB_DATA;
463 // adjust the new fragment size
464 fragment.ReleaseBuffer(SB_FRAG_HEADER_SIZE + todo);
466 // return next round
467 return nextOffset;
470 void Socket::CheckSequence(const Data &seq)
472 // if( m_socket == 0 ) {
473 // // don't do any sequence checking on socket 0
474 // return;
475 // }
477 MAKE_PACKET(spack, seq);
478 if( (unsigned int) seq.GetSize() < SB_SEQUENCE_PACKET_SIZE ) {
479 eout("Short sequence packet:\n" << seq);
480 throw Error("Socket: invalid sequence packet");
483 // we'll cheat here... if the packet's sequence is 0, we'll
484 // silently restart, otherwise, fail
485 uint32_t sequenceId = btohl(spack->u.sequence.sequenceId);
486 if( sequenceId == 0 ) {
487 // silently restart (will advance below)
488 m_sequenceId = 0;
490 else {
491 if( sequenceId != m_sequenceId ) {
492 if( m_socket != 0 ) {
493 eout("Socket sequence: " << m_sequenceId
494 << ". Packet sequence: " << sequenceId);
495 throw Error("Socket: out of sequence");
497 else {
498 dout("Bad sequence on socket 0: expected: "
499 << msequenceId
500 << ". Packet sequence: " << sequenceId);
505 // advance!
506 m_sequenceId++;
509 // sends the send packet down to the device, fragmenting if
510 // necessary, and returns the response in receive, defragmenting
511 // if needed
512 // Blocks until response received or timed out in Usb::Device
513 void Socket::Packet(Data &send, Data &receive, int timeout)
516 // FIXME - this might be a good idea someday, or perhaps provide a wrapper
517 // function that forces the socket number to the correct current value,
518 // but putting it here means a copy on every packet.
520 // force socket to our socket
521 Data send = sendorig;
522 Barry::Protocol::Packet *sspack = (Barry::Protocol::Packet *)send.GetBuffer(2);
523 sspack->socket = htobs(GetSocket());
526 MAKE_PACKET(spack, send);
527 if( send.GetSize() < MIN_PACKET_SIZE ||
528 (spack->command != SB_COMMAND_DB_DATA &&
529 spack->command != SB_COMMAND_DB_DONE) )
531 // we don't do that around here
532 throw std::logic_error("Socket: unknown send data in Packet()");
535 Data inFrag;
536 receive.Zap();
538 if( send.GetSize() <= MAX_PACKET_SIZE ) {
539 // send non-fragmented
540 Send(send, inFrag, timeout);
542 else {
543 // send fragmented
544 unsigned int offset = 0;
545 Data outFrag;
547 do {
548 offset = MakeNextFragment(send, outFrag, offset);
549 Send(outFrag, inFrag, timeout);
551 MAKE_PACKET(rpack, inFrag);
552 // only process sequence handshakes... once we
553 // get to the last fragment, we fall through to normal
554 // processing below
555 if( offset && inFrag.GetSize() > 0 ) {
557 Protocol::CheckSize(inFrag);
559 switch( rpack->command )
561 case SB_COMMAND_SEQUENCE_HANDSHAKE:
562 CheckSequence(inFrag);
563 break;
565 default:
566 eout("Command: " << std::setbase(16) << (unsigned int)rpack->command << inFrag);
567 throw Error("Socket: unhandled packet in Packet()");
568 break;
573 } while( offset > 0 );
576 bool done = false, frag = false;
577 int blankCount = 0;
578 while( !done ) {
579 MAKE_PACKET(rpack, inFrag);
581 // check the packet's validity
582 if( inFrag.GetSize() > 0 ) {
583 blankCount = 0;
585 Protocol::CheckSize(inFrag);
587 switch( rpack->command )
589 case SB_COMMAND_SEQUENCE_HANDSHAKE:
590 CheckSequence(inFrag);
591 break;
593 case SB_COMMAND_DB_DATA:
594 if( frag ) {
595 AppendFragment(receive, inFrag);
597 else {
598 receive = inFrag;
600 done = true;
601 break;
603 case SB_COMMAND_DB_FRAGMENTED:
604 AppendFragment(receive, inFrag);
605 frag = true;
606 break;
608 case SB_COMMAND_DB_DONE:
609 receive = inFrag;
610 done = true;
611 break;
613 default:
614 eout("Command: " << std::setbase(16) << (unsigned int)rpack->command << inFrag);
615 throw Error("Socket: unhandled packet in Packet()");
616 break;
619 else {
620 blankCount++;
621 //std::cerr << "Blank! " << blankCount << std::endl;
622 if( blankCount == 10 ) {
623 // only ask for more data on stalled sockets
624 // for so long
625 throw Error("Socket: 10 blank packets received");
629 if( !done ) {
630 // not done yet, ask for another read
631 Receive(inFrag);
636 void Socket::Packet(Barry::Packet &packet, int timeout)
638 Packet(packet.m_send, packet.m_receive, timeout);
641 void Socket::NextRecord(Data &receive)
643 Barry::Protocol::Packet packet;
644 packet.socket = htobs(GetSocket());
645 packet.size = htobs(7);
646 packet.command = SB_COMMAND_DB_DONE;
647 packet.u.db.tableCmd = 0;
648 packet.u.db.u.command.operation = 0;
650 Data command(&packet, 7);
651 Packet(command, receive);
655 } // namespace Barry