Release tarball for barry-0.9
[barry.git] / src / socket.cc
blob041e23c5ffaa2f70491369b4c2fe363d247ed8f4
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>
32 #include <sstream>
35 using namespace Usb;
38 namespace Barry {
40 Socket::Socket( Device &dev,
41 int writeEndpoint, int readEndpoint,
42 uint8_t zeroSocketSequenceStart)
43 : m_dev(dev),
44 m_writeEp(writeEndpoint),
45 m_readEp(readEndpoint),
46 m_socket(0),
47 m_zeroSocketSequence(zeroSocketSequenceStart),
48 m_flag(0),
49 m_sequenceId(0),
50 m_halfOpen(false),
51 m_challengeSeed(0),
52 m_remainingTries(0)
56 Socket::~Socket()
58 // trap exceptions in the destructor
59 try {
60 // a non-default socket has been opened, close it
61 Close();
63 catch( std::runtime_error &re ) {
64 // do nothing... log it?
65 dout("Exception caught in ~Socket: " << re.what());
69 void Socket::SendOpen(uint16_t socket, Data &receive)
71 // build open command
72 Barry::Protocol::Packet packet;
73 packet.socket = 0;
74 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
75 packet.command = SB_COMMAND_OPEN_SOCKET;
76 packet.u.socket.socket = htobs(socket);
77 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
79 // save sequence for later close, and inc
80 m_flag = m_zeroSocketSequence;
82 Data send(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
83 try {
84 Send(send, receive);
85 } catch( Usb::Error & ) {
86 eeout(send, receive);
87 throw;
90 // check sequence ID
91 Protocol::CheckSize(receive);
92 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
93 CheckSequence(receive);
95 // still need our ACK
96 Receive(receive);
99 // receive now holds the Open response
102 // SHA1 hashing logic based on Rick Scott's XmBlackBerry's send_password()
103 void Socket::SendPasswordHash(uint16_t socket, const char *password, Data &receive)
105 unsigned char pwdigest[SHA_DIGEST_LENGTH];
106 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
108 // first, hash the password by itself
109 SHA1((unsigned char *) password, strlen(password), pwdigest);
111 // prefix the resulting hash with the provided seed
112 uint32_t seed = htobl(m_challengeSeed);
113 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
114 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
116 // hash again
117 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
120 size_t size = SB_SOCKET_PACKET_HEADER_SIZE + PASSWORD_CHALLENGE_SIZE;
122 // build open command
123 Barry::Protocol::Packet packet;
124 packet.socket = 0;
125 packet.size = htobs(size);
126 packet.command = SB_COMMAND_PASSWORD;
127 packet.u.socket.socket = htobs(socket);
128 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
129 packet.u.socket.u.password.remaining_tries = 0;
130 packet.u.socket.u.password.unknown = 0;
131 packet.u.socket.u.password.param = htobs(0x14); // FIXME - what does this mean?
132 memcpy(packet.u.socket.u.password.u.hash, pwdigest,
133 sizeof(packet.u.socket.u.password.u.hash));
135 // blank password hashes as we don't need these anymore
136 memset(pwdigest, 0, sizeof(pwdigest));
137 memset(prefixedhash, 0, sizeof(prefixedhash));
139 // save sequence for later close, and inc
140 m_flag = m_zeroSocketSequence;
142 Data send(&packet, size);
143 Send(send, receive);
145 // blank password hash as we don't need this anymore either
146 memset(packet.u.socket.u.password.u.hash, 0,
147 sizeof(packet.u.socket.u.password.u.hash));
148 send.Zap();
150 // check sequence ID
151 Protocol::CheckSize(receive);
152 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
153 CheckSequence(receive);
155 // still need our ACK
156 Receive(receive);
159 // receive now holds the Password response
164 // Open
166 /// Open a logical socket on the device.
168 /// Both the socket number and the flag are based on the response to the
169 /// SELECT_MODE command. See Controller::SelectMode() for more info
170 /// on this.
172 /// The packet sequence is normal for most socket operations.
174 /// - Down: command packet with OPEN_SOCKET
175 /// - Up: optional sequence handshake packet
176 /// - Up: command response, which repeats the socket and flag data
177 /// as confirmation
179 /// \exception Barry::Error
180 /// Thrown on protocol error.
182 /// \exception Barry::BadPassword
183 /// Thrown on invalid password, or not enough retries left
184 /// on device.
186 void Socket::Open(uint16_t socket, const char *password)
188 if( m_socket != 0 ) {
189 // already open
190 throw Error("Socket: already open");
193 // Things get a little funky here, as we may be left in an
194 // intermediate state in the case of a failed password.
195 // This function should support being called as many times
196 // as needed to handle the password
198 Data send, receive;
199 ZeroPacket packet(send, receive);
201 if( !m_halfOpen ) {
202 // starting fresh
203 m_remainingTries = 0;
205 SendOpen(socket, receive);
207 // check for password challenge, or success
208 if( packet.Command() == SB_COMMAND_PASSWORD_CHALLENGE ) {
209 m_halfOpen = true;
210 m_challengeSeed = packet.ChallengeSeed();
211 m_remainingTries = packet.RemainingTries();
214 // fall through to challenge code...
217 if( m_halfOpen ) {
218 // half open, device is expecting a password hash... do we
219 // have a password?
220 if( !password ) {
221 throw BadPassword("No password specified.", m_remainingTries, false);
224 // only allow password attempts if there are 6 or more
225 // tries remaining... we want to give the user at least
226 // 5 chances on a Windows machine before the device
227 // commits suicide.
228 if( m_remainingTries < 6 ) {
229 throw BadPassword("Fewer than 6 password tries "
230 "remaining in device. Refusing to proceed, "
231 "to avoid device zapping itself. Use a "
232 "Windows client, or re-cradle the device.",
233 m_remainingTries,
234 true);
237 SendPasswordHash(socket, password, receive);
239 if( packet.Command() == SB_COMMAND_PASSWORD_FAILED ) {
240 m_halfOpen = true;
241 m_challengeSeed = packet.ChallengeSeed();
242 m_remainingTries = packet.RemainingTries();
243 throw BadPassword("Password rejected by device.", m_remainingTries, false);
246 // if we get this far, we are no longer in half-open password
247 // mode, so we can reset our flags
248 m_halfOpen = false;
250 // fall through to success check...
253 if( packet.Command() != SB_COMMAND_OPENED_SOCKET ||
254 packet.SocketResponse() != socket ||
255 packet.SocketSequence() != m_flag )
257 eout("Packet:\n" << receive);
258 throw Error("Socket: Bad OPENED packet in Open");
261 // success! save the socket
262 m_socket = socket;
266 // Close
268 /// Closes a non-default socket (i.e. non-zero socket number)
270 /// The packet sequence is just like Open(), except the command is
271 /// CLOSE_SOCKET.
273 /// \exception Barry::Error
275 void Socket::Close()
277 if( m_socket != 0 ) {
278 // only close non-default sockets
280 // build close command
281 Barry::Protocol::Packet packet;
282 packet.socket = 0;
283 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
284 packet.command = SB_COMMAND_CLOSE_SOCKET;
285 packet.u.socket.socket = htobs(m_socket);
286 packet.u.socket.sequence = m_flag;
288 Data command(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
289 Data response;
290 try {
291 Send(command, response);
293 catch( Usb::Error & ) {
294 // reset so this won't be called again
295 m_socket = 0;
296 m_flag = 0;
298 eeout(command, response);
299 throw;
302 // starting fresh, reset sequence ID
303 Protocol::CheckSize(response);
304 if( IS_COMMAND(response, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
305 CheckSequence(response);
307 // still need our ACK
308 Receive(response);
311 Protocol::CheckSize(response, SB_SOCKET_PACKET_HEADER_SIZE);
312 MAKE_PACKET(rpack, response);
313 if( rpack->command != SB_COMMAND_CLOSED_SOCKET ||
314 btohs(rpack->u.socket.socket) != m_socket ||
315 rpack->u.socket.sequence != m_flag )
317 // reset so this won't be called again
318 m_socket = 0;
319 m_flag = 0;
321 eout("Packet:\n" << response);
322 throw Error("Socket: Bad CLOSED packet in Close");
325 // // and finally, there always seems to be an extra read of
326 // // an empty packet at the end... just throw it away
327 // try {
328 // Receive(response, 1);
329 // }
330 // catch( Usb::Timeout & ) {
331 // }
333 // reset socket and flag
334 m_socket = 0;
335 m_flag = 0;
341 // Send
343 /// Sends 'send' data to device, no receive.
345 /// \returns void
347 /// \exception Usb::Error on underlying bus errors.
349 void Socket::Send(Data &send, int timeout)
351 // Special case: it seems that sending packets with a size that's an
352 // exact multiple of 0x40 causes the device to get confused.
354 // To get around that, it is observed in the captures that the size
355 // is sent in a special 3 byte packet before the real packet.
356 // Check for this case here.
358 if( (send.GetSize() % 0x40) == 0 ) {
359 Protocol::SizePacket packet;
360 packet.size = htobs(send.GetSize());
361 packet.buffer[2] = 0; // zero the top byte
362 Data sizeCommand(&packet, 3);
364 m_dev.BulkWrite(m_writeEp, sizeCommand);
367 // If this is a socket 0 packet, force the send packet data's
368 // socket 0 sequence number to something correct.
369 if( m_socket == 0 && send.GetSize() >= SB_SOCKET_PACKET_HEADER_SIZE ) {
370 MAKE_PACKETPTR_BUF(spack, send.GetBuffer());
371 spack->u.socket.sequence = m_zeroSocketSequence;
372 m_zeroSocketSequence++;
375 m_dev.BulkWrite(m_writeEp, send);
379 // Send
381 /// Sends 'send' data to device, and waits for response.
383 /// \returns void
385 /// \exception Usb::Error on underlying bus errors.
387 void Socket::Send(Data &send, Data &receive, int timeout)
389 Send(send, timeout);
390 m_dev.BulkRead(m_readEp, receive, timeout);
391 ddout("Socket::Send: Endpoint " << m_readEp << "\nReceived:\n" << receive);
394 void Socket::Send(Barry::Packet &packet, int timeout)
396 Send(packet.m_send, packet.m_receive, timeout);
399 void Socket::Receive(Data &receive, int timeout)
401 m_dev.BulkRead(m_readEp, receive, timeout);
403 ddout("Socket::Receive: Endpoint " << m_readEp << "\nReceived:\n" << receive);
406 // appends fragment to whole... if whole is empty, simply copies, and
407 // sets command to DATA instead of FRAGMENTED. Always updates the
408 // packet size of whole, to reflect the total size
409 void Socket::AppendFragment(Data &whole, const Data &fragment)
411 if( whole.GetSize() == 0 ) {
412 // empty, so just copy
413 whole = fragment;
415 else {
416 // has some data already, so just append
417 int size = whole.GetSize();
418 unsigned char *buf = whole.GetBuffer(size + fragment.GetSize());
419 MAKE_PACKET(fpack, fragment);
420 int fragsize = fragment.GetSize() - SB_FRAG_HEADER_SIZE;
422 memcpy(buf+size, &fpack->u.db.u.fragment, fragsize);
423 whole.ReleaseBuffer(size + fragsize);
426 // update whole's size and command type for future sanity
427 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) whole.GetBuffer();
428 wpack->size = htobs((uint16_t) whole.GetSize());
429 wpack->command = SB_COMMAND_DB_DATA;
430 // don't need to call ReleaseBuffer here, since we're not changing
431 // the real size size
434 // If offset is 0, starts fresh, taking the first fragment packet size chunk
435 // out of whole and creating a sendable packet in fragment. Returns the
436 // next offset if there is still more data, or 0 if finished.
437 unsigned int Socket::MakeNextFragment(const Data &whole, Data &fragment, unsigned int offset)
439 // sanity check
440 if( whole.GetSize() < SB_FRAG_HEADER_SIZE ) {
441 eout("Whole packet too short to fragment: " << whole.GetSize());
442 throw Error("Socket: Whole packet too short to fragment");
445 // calculate size
446 unsigned int todo = whole.GetSize() - SB_FRAG_HEADER_SIZE - offset;
447 unsigned int nextOffset = 0;
448 if( todo > (MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE) ) {
449 todo = MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE;
450 nextOffset = offset + todo;
453 // create fragment header
454 unsigned char *buf = fragment.GetBuffer(SB_FRAG_HEADER_SIZE + todo);
455 memcpy(buf, whole.GetData(), SB_FRAG_HEADER_SIZE);
457 // copy over a fragment size of data
458 memcpy(buf + SB_FRAG_HEADER_SIZE, whole.GetData() + SB_FRAG_HEADER_SIZE + offset, todo);
460 // update fragment's size and command type
461 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) buf;
462 wpack->size = htobs((uint16_t) (todo + SB_FRAG_HEADER_SIZE));
463 if( nextOffset )
464 wpack->command = SB_COMMAND_DB_FRAGMENTED;
465 else
466 wpack->command = SB_COMMAND_DB_DATA;
468 // adjust the new fragment size
469 fragment.ReleaseBuffer(SB_FRAG_HEADER_SIZE + todo);
471 // return next round
472 return nextOffset;
475 void Socket::CheckSequence(const Data &seq)
477 // if( m_socket == 0 ) {
478 // // don't do any sequence checking on socket 0
479 // return;
480 // }
482 MAKE_PACKET(spack, seq);
483 if( (unsigned int) seq.GetSize() < SB_SEQUENCE_PACKET_SIZE ) {
484 eout("Short sequence packet:\n" << seq);
485 throw Error("Socket: invalid sequence packet");
488 // we'll cheat here... if the packet's sequence is 0, we'll
489 // silently restart, otherwise, fail
490 uint32_t sequenceId = btohl(spack->u.sequence.sequenceId);
491 if( sequenceId == 0 ) {
492 // silently restart (will advance below)
493 m_sequenceId = 0;
495 else {
496 if( sequenceId != m_sequenceId ) {
497 if( m_socket != 0 ) {
498 eout("Socket sequence: " << m_sequenceId
499 << ". Packet sequence: " << sequenceId);
500 throw Error("Socket: out of sequence");
502 else {
503 dout("Bad sequence on socket 0: expected: "
504 << msequenceId
505 << ". Packet sequence: " << sequenceId);
510 // advance!
511 m_sequenceId++;
514 // sends the send packet down to the device, fragmenting if
515 // necessary, and returns the response in receive, defragmenting
516 // if needed
517 // Blocks until response received or timed out in Usb::Device
518 void Socket::Packet(Data &send, Data &receive, int timeout)
521 // FIXME - this might be a good idea someday, or perhaps provide a wrapper
522 // function that forces the socket number to the correct current value,
523 // but putting it here means a copy on every packet.
525 // force socket to our socket
526 Data send = sendorig;
527 Barry::Protocol::Packet *sspack = (Barry::Protocol::Packet *)send.GetBuffer(2);
528 sspack->socket = htobs(GetSocket());
531 MAKE_PACKET(spack, send);
532 if( send.GetSize() < MIN_PACKET_SIZE ||
533 (spack->command != SB_COMMAND_DB_DATA &&
534 spack->command != SB_COMMAND_DB_DONE) )
536 // we don't do that around here
537 throw std::logic_error("Socket: unknown send data in Packet()");
540 Data inFrag;
541 receive.Zap();
543 if( send.GetSize() <= MAX_PACKET_SIZE ) {
544 // send non-fragmented
545 Send(send, inFrag, timeout);
547 else {
548 // send fragmented
549 unsigned int offset = 0;
550 Data outFrag;
552 do {
553 offset = MakeNextFragment(send, outFrag, offset);
554 Send(outFrag, inFrag, timeout);
556 MAKE_PACKET(rpack, inFrag);
557 // only process sequence handshakes... once we
558 // get to the last fragment, we fall through to normal
559 // processing below
560 if( offset && inFrag.GetSize() > 0 ) {
562 Protocol::CheckSize(inFrag);
564 switch( rpack->command )
566 case SB_COMMAND_SEQUENCE_HANDSHAKE:
567 CheckSequence(inFrag);
568 break;
570 default: {
571 std::ostringstream oss;
572 oss << "Socket: unhandled packet in Packet() (send): 0x" << std::hex << (unsigned int)rpack->command;
573 eout(oss.str());
574 throw Error(oss.str());
576 break;
581 } while( offset > 0 );
584 bool done = false, frag = false;
585 int blankCount = 0;
586 while( !done ) {
587 MAKE_PACKET(rpack, inFrag);
589 // check the packet's validity
590 if( inFrag.GetSize() > 0 ) {
591 blankCount = 0;
593 Protocol::CheckSize(inFrag);
595 switch( rpack->command )
597 case SB_COMMAND_SEQUENCE_HANDSHAKE:
598 CheckSequence(inFrag);
599 break;
601 case SB_COMMAND_DB_DATA:
602 if( frag ) {
603 AppendFragment(receive, inFrag);
605 else {
606 receive = inFrag;
608 done = true;
609 break;
611 case SB_COMMAND_DB_FRAGMENTED:
612 AppendFragment(receive, inFrag);
613 frag = true;
614 break;
616 case SB_COMMAND_DB_DONE:
617 receive = inFrag;
618 done = true;
619 break;
621 default: {
622 std::ostringstream oss;
623 oss << "Socket: unhandled packet in Packet() (read): 0x" << std::hex << (unsigned int)rpack->command;
624 eout(oss.str());
625 throw Error(oss.str());
627 break;
630 else {
631 blankCount++;
632 //std::cerr << "Blank! " << blankCount << std::endl;
633 if( blankCount == 10 ) {
634 // only ask for more data on stalled sockets
635 // for so long
636 throw Error("Socket: 10 blank packets received");
640 if( !done ) {
641 // not done yet, ask for another read
642 Receive(inFrag);
647 void Socket::Packet(Barry::Packet &packet, int timeout)
649 Packet(packet.m_send, packet.m_receive, timeout);
652 void Socket::NextRecord(Data &receive)
654 Barry::Protocol::Packet packet;
655 packet.socket = htobs(GetSocket());
656 packet.size = htobs(7);
657 packet.command = SB_COMMAND_DB_DONE;
658 packet.u.db.tableCmd = 0;
659 packet.u.db.u.command.operation = 0;
661 Data command(&packet, 7);
662 Packet(command, receive);
666 } // namespace Barry