Handle erase and loading of in use modules
[barry.git] / src / socket.cc
blob77d3124ae0df521a0a4b3fa8eabeb4e1d15459ef
1 ///
2 /// \file socket.cc
3 /// Class wrapper to encapsulate the Blackberry USB logical socket
4 ///
6 /*
7 Copyright (C) 2005-2009, 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 "data.h"
25 #include "protocol.h"
26 #include "protostructs.h"
27 #include "endian.h"
28 #include "debug.h"
29 #include "packet.h"
30 #include "sha1.h"
31 #include <sstream>
32 #include <string.h>
34 using namespace Usb;
37 namespace Barry {
40 //////////////////////////////////////////////////////////////////////////////
41 // SocketZero class
43 SocketZero::SocketZero( SocketRoutingQueue &queue,
44 int writeEndpoint,
45 uint8_t zeroSocketSequenceStart)
46 : m_dev(0),
47 m_queue(&queue),
48 m_writeEp(writeEndpoint),
49 m_readEp(0),
50 m_zeroSocketSequence(zeroSocketSequenceStart),
51 m_sequenceId(0),
52 m_halfOpen(false),
53 m_challengeSeed(0),
54 m_remainingTries(0),
55 m_sequencePacket(true)
59 SocketZero::SocketZero( Device &dev,
60 int writeEndpoint, int readEndpoint,
61 uint8_t zeroSocketSequenceStart)
62 : m_dev(&dev),
63 m_queue(0),
64 m_writeEp(writeEndpoint),
65 m_readEp(readEndpoint),
66 m_zeroSocketSequence(zeroSocketSequenceStart),
67 m_sequenceId(0),
68 m_halfOpen(false),
69 m_challengeSeed(0),
70 m_remainingTries(0),
71 m_sequencePacket(true),
72 m_resetOnClose(false)
76 SocketZero::~SocketZero()
78 // nothing to close for socket zero
82 ///////////////////////////////////////
83 // Socket Zero static calls
85 // appends fragment to whole... if whole is empty, simply copies, and
86 // sets command to DATA instead of FRAGMENTED. Always updates the
87 // packet size of whole, to reflect the total size
88 void SocketZero::AppendFragment(Data &whole, const Data &fragment)
90 if( whole.GetSize() == 0 ) {
91 // empty, so just copy
92 whole = fragment;
94 else {
95 // has some data already, so just append
96 int size = whole.GetSize();
97 unsigned char *buf = whole.GetBuffer(size + fragment.GetSize());
98 MAKE_PACKET(fpack, fragment);
99 int fragsize = fragment.GetSize() - SB_FRAG_HEADER_SIZE;
101 memcpy(buf+size, &fpack->u.db.u.fragment, fragsize);
102 whole.ReleaseBuffer(size + fragsize);
105 // update whole's size and command type for future sanity
106 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) whole.GetBuffer();
107 wpack->size = htobs((uint16_t) whole.GetSize());
108 wpack->command = SB_COMMAND_DB_DATA;
109 // don't need to call ReleaseBuffer here, since we're not changing
110 // the real data size, and ReleaseBuffer was called above during copy
113 // If offset is 0, starts fresh, taking the first fragment packet size chunk
114 // out of whole and creating a sendable packet in fragment. Returns the
115 // next offset if there is still more data, or 0 if finished.
116 unsigned int SocketZero::MakeNextFragment(const Data &whole, Data &fragment, unsigned int offset)
118 // sanity check
119 if( whole.GetSize() < SB_FRAG_HEADER_SIZE ) {
120 eout("Whole packet too short to fragment: " << whole.GetSize());
121 throw Error("Socket: Whole packet too short to fragment");
124 // calculate size
125 unsigned int todo = whole.GetSize() - SB_FRAG_HEADER_SIZE - offset;
126 unsigned int nextOffset = 0;
127 if( todo > (MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE) ) {
128 todo = MAX_PACKET_SIZE - SB_FRAG_HEADER_SIZE;
129 nextOffset = offset + todo;
132 // create fragment header
133 unsigned char *buf = fragment.GetBuffer(SB_FRAG_HEADER_SIZE + todo);
134 memcpy(buf, whole.GetData(), SB_FRAG_HEADER_SIZE);
136 // copy over a fragment size of data
137 memcpy(buf + SB_FRAG_HEADER_SIZE, whole.GetData() + SB_FRAG_HEADER_SIZE + offset, todo);
139 // update fragment's size and command type
140 Barry::Protocol::Packet *wpack = (Barry::Protocol::Packet *) buf;
141 wpack->size = htobs((uint16_t) (todo + SB_FRAG_HEADER_SIZE));
142 if( nextOffset )
143 wpack->command = SB_COMMAND_DB_FRAGMENTED;
144 else
145 wpack->command = SB_COMMAND_DB_DATA;
147 // adjust the new fragment size
148 fragment.ReleaseBuffer(SB_FRAG_HEADER_SIZE + todo);
150 // return next round
151 return nextOffset;
155 ///////////////////////////////////////
156 // SocketZero private API
159 // FIXME - not sure yet whether sequence ID's are per socket or not... if
160 // they are per socket, then this global sequence behaviour will not work,
161 // and we need to track m_sequenceId on a Socket level.
163 void SocketZero::CheckSequence(uint16_t socket, const Data &seq)
165 MAKE_PACKET(spack, seq);
166 if( (unsigned int) seq.GetSize() < SB_SEQUENCE_PACKET_SIZE ) {
167 eout("Short sequence packet:\n" << seq);
168 throw Error("Socket: invalid sequence packet");
171 // we'll cheat here... if the packet's sequence is 0, we'll
172 // silently restart, otherwise, fail
173 uint32_t sequenceId = btohl(spack->u.sequence.sequenceId);
174 if( sequenceId == 0 ) {
175 // silently restart (will advance below)
176 m_sequenceId = 0;
178 else {
179 if( sequenceId != m_sequenceId ) {
180 if( socket != 0 ) {
181 std::ostringstream oss;
182 oss << "Socket 0x" << std::hex << (unsigned int)socket
183 << ": out of sequence. "
184 << "(Global sequence: " << m_sequenceId
185 << ". Packet sequence: " << sequenceId
186 << ")";
187 eout(oss.str());
188 throw Error(oss.str());
190 else {
191 dout("Bad sequence on socket 0: expected: "
192 << msequenceId
193 << ". Packet sequence: " << sequenceId);
198 // advance!
199 m_sequenceId++;
202 void SocketZero::SendOpen(uint16_t socket, Data &receive)
204 // build open command
205 Barry::Protocol::Packet packet;
206 packet.socket = 0;
207 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
208 packet.command = SB_COMMAND_OPEN_SOCKET;
209 packet.u.socket.socket = htobs(socket);
210 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
212 Data send(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
213 try {
214 RawSend(send);
215 RawReceive(receive);
216 } catch( Usb::Error & ) {
217 eeout(send, receive);
218 throw;
221 // check sequence ID
222 Protocol::CheckSize(receive, SB_PACKET_HEADER_SIZE);
223 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
224 CheckSequence(0, receive);
226 // still need our ACK
227 RawReceive(receive);
230 // receive now holds the Open response
233 // SHA1 hashing logic based on Rick Scott's XmBlackBerry's send_password()
234 void SocketZero::SendPasswordHash(uint16_t socket, const char *password, Data &receive)
236 unsigned char pwdigest[SHA_DIGEST_LENGTH];
237 unsigned char prefixedhash[SHA_DIGEST_LENGTH + 4];
239 // first, hash the password by itself
240 SHA1((unsigned char *) password, strlen(password), pwdigest);
242 // prefix the resulting hash with the provided seed
243 uint32_t seed = htobl(m_challengeSeed);
244 memcpy(&prefixedhash[0], &seed, sizeof(uint32_t));
245 memcpy(&prefixedhash[4], pwdigest, SHA_DIGEST_LENGTH);
247 // hash again
248 SHA1((unsigned char *) prefixedhash, SHA_DIGEST_LENGTH + 4, pwdigest);
251 size_t size = SB_SOCKET_PACKET_HEADER_SIZE + PASSWORD_CHALLENGE_SIZE;
253 // build open command
254 Barry::Protocol::Packet packet;
255 packet.socket = 0;
256 packet.size = htobs(size);
257 packet.command = SB_COMMAND_PASSWORD;
258 packet.u.socket.socket = htobs(socket);
259 packet.u.socket.sequence = m_zeroSocketSequence;// overwritten by Send()
260 packet.u.socket.u.password.remaining_tries = 0;
261 packet.u.socket.u.password.unknown = 0;
262 packet.u.socket.u.password.param = htobs(0x14); // FIXME - what does this mean?
263 memcpy(packet.u.socket.u.password.u.hash, pwdigest,
264 sizeof(packet.u.socket.u.password.u.hash));
266 // blank password hashes as we don't need these anymore
267 memset(pwdigest, 0, sizeof(pwdigest));
268 memset(prefixedhash, 0, sizeof(prefixedhash));
270 Data send(&packet, size);
271 RawSend(send);
272 RawReceive(receive);
274 // blank password hash as we don't need this anymore either
275 memset(packet.u.socket.u.password.u.hash, 0,
276 sizeof(packet.u.socket.u.password.u.hash));
277 send.Zap();
279 // check sequence ID
280 Protocol::CheckSize(receive, SB_PACKET_HEADER_SIZE);
281 if( IS_COMMAND(receive, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
282 CheckSequence(0, receive);
284 // still need our ACK
285 RawReceive(receive);
288 // receive now holds the Password response
291 void SocketZero::RawSend(Data &send, int timeout)
293 Usb::Device *dev = m_queue ? m_queue->GetUsbDevice() : m_dev;
295 // Special case: it seems that sending packets with a size that's an
296 // exact multiple of 0x40 causes the device to get confused.
298 // To get around that, it is observed in the captures that the size
299 // is sent in a special 3 byte packet before the real packet.
300 // Check for this case here.
302 if( (send.GetSize() % 0x40) == 0 ) {
303 Protocol::SizePacket packet;
304 packet.size = htobs(send.GetSize());
305 packet.buffer[2] = 0; // zero the top byte
306 Data sizeCommand(&packet, 3);
308 dev->BulkWrite(m_writeEp, sizeCommand);
311 dev->BulkWrite(m_writeEp, send);
314 void SocketZero::RawReceive(Data &receive, int timeout)
316 do {
317 if( m_queue ) {
318 if( !m_queue->DefaultRead(receive, timeout) )
319 throw Timeout("SocketZero::RawReceive: queue DefaultRead returned false (likely a timeout)");
321 else {
322 m_dev->BulkRead(m_readEp, receive, timeout);
324 ddout("SocketZero::RawReceive: Endpoint " << m_readEp
325 << "\nReceived:\n" << receive);
326 } while( SequencePacket(receive) );
330 // SequencePacket
332 /// Returns true if this is a sequence packet that should be ignored.
333 /// This function is used in SocketZero::RawReceive() in order
334 /// to determine whether to keep reading or not. By default,
335 /// this function checks whether the packet is a sequence packet
336 /// or not, and returns true if so. Also, if it is a sequence
337 /// packet, it checks the validity of the sequence number.
339 /// If sequence packets become important in the future, this
340 /// function could be changed to call a user-defined callback,
341 /// in order to handle these things out of band.
343 bool SocketZero::SequencePacket(const Data &data)
345 // Begin -- Test quiet durty :(
346 if (m_sequencePacket == false) {
347 return false;
349 // End -- Test quiet durty :(
351 if( data.GetSize() >= MIN_PACKET_SIZE ) {
352 MAKE_PACKET(rpack, data);
353 if( rpack->socket == 0 &&
354 rpack->command == SB_COMMAND_SEQUENCE_HANDSHAKE )
356 CheckSequence(0, data);
357 return true;
360 return false; // not a sequence packet
364 ///////////////////////////////////////
365 // SocketZero public API
367 void SocketZero::SetRoutingQueue(SocketRoutingQueue &queue)
369 // replace the current queue pointer
370 m_queue = &queue;
373 void SocketZero::UnlinkRoutingQueue()
375 m_queue = 0;
378 void SocketZero::Send(Data &send, int timeout)
380 // force the socket number to 0
381 if( send.GetSize() >= SB_SOCKET_PACKET_HEADER_SIZE ) {
382 MAKE_PACKETPTR_BUF(spack, send.GetBuffer());
383 spack->socket = 0;
386 // This is a socket 0 packet, so force the send packet data's
387 // socket 0 sequence number to something correct.
388 if( send.GetSize() >= SB_SOCKET_PACKET_HEADER_SIZE ) {
389 MAKE_PACKETPTR_BUF(spack, send.GetBuffer());
390 spack->u.socket.sequence = m_zeroSocketSequence;
391 m_zeroSocketSequence++;
394 RawSend(send, timeout);
397 void SocketZero::Send(Data &send, Data &receive, int timeout)
399 Send(send, timeout);
400 RawReceive(receive, timeout);
403 void SocketZero::Send(Barry::Packet &packet, int timeout)
405 Send(packet.m_send, packet.m_receive, timeout);
408 void SocketZero::Receive(Data &receive, int timeout)
410 RawReceive(receive, timeout);
415 // Open
417 /// Open a logical socket on the device.
419 /// Both the socket number and the flag are based on the response to the
420 /// SELECT_MODE command. See Controller::SelectMode() for more info
421 /// on this.
423 /// The packet sequence is normal for most socket operations.
425 /// - Down: command packet with OPEN_SOCKET
426 /// - Up: optional sequence handshake packet
427 /// - Up: command response, which repeats the socket and flag data
428 /// as confirmation
430 /// \exception Barry::Error
431 /// Thrown on protocol error.
433 /// \exception Barry::BadPassword
434 /// Thrown on invalid password, or not enough retries left
435 /// on device.
437 SocketHandle SocketZero::Open(uint16_t socket, const char *password)
439 // Things get a little funky here, as we may be left in an
440 // intermediate state in the case of a failed password.
441 // This function should support being called as many times
442 // as needed to handle the password
444 Data send, receive;
445 ZeroPacket packet(send, receive);
447 // save sequence for later close
448 uint8_t closeFlag = GetZeroSocketSequence();
450 if( !m_halfOpen ) {
451 // starting fresh
452 m_remainingTries = 0;
454 SendOpen(socket, receive);
456 // check for password challenge, or success
457 if( packet.Command() == SB_COMMAND_PASSWORD_CHALLENGE ) {
458 m_halfOpen = true;
459 m_challengeSeed = packet.ChallengeSeed();
460 m_remainingTries = packet.RemainingTries();
463 // fall through to challenge code...
466 if( m_halfOpen ) {
467 // half open, device is expecting a password hash... do we
468 // have a password?
469 if( !password ) {
470 throw BadPassword("No password specified.", m_remainingTries, false);
473 // only allow password attempts if there are
474 // BARRY_MIN_PASSWORD_TRIES or more tries remaining...
475 // we want to give the user at least some chance on a
476 // Windows machine before the device commits suicide.
477 if( m_remainingTries < BARRY_MIN_PASSWORD_TRIES ) {
478 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.",
479 m_remainingTries,
480 true);
483 // save sequence for later close (again after SendOpen())
484 closeFlag = GetZeroSocketSequence();
486 SendPasswordHash(socket, password, receive);
488 if( packet.Command() == SB_COMMAND_PASSWORD_FAILED ) {
489 m_halfOpen = true;
490 m_challengeSeed = packet.ChallengeSeed();
491 m_remainingTries = packet.RemainingTries();
492 throw BadPassword("Password rejected by device.", m_remainingTries, false);
495 // if we get this far, we are no longer in half-open password
496 // mode, so we can reset our flags
497 m_halfOpen = false;
499 // fall through to success check...
502 if( packet.Command() != SB_COMMAND_OPENED_SOCKET ||
503 packet.SocketResponse() != socket ||
504 packet.SocketSequence() != closeFlag )
506 eout("Packet:\n" << receive);
507 throw Error("Socket: Bad OPENED packet in Open");
510 // success! save the socket
511 return SocketHandle(new Socket(*this, socket, closeFlag));
515 // Close
517 /// Closes a non-default socket (i.e. non-zero socket number)
519 /// The packet sequence is just like Open(), except the command is
520 /// CLOSE_SOCKET.
522 /// \exception Barry::Error
524 void SocketZero::Close(Socket &socket)
526 if( socket.GetSocket() == 0 )
527 return; // nothing to do
529 // build close command
530 Barry::Protocol::Packet packet;
531 packet.socket = 0;
532 packet.size = htobs(SB_SOCKET_PACKET_HEADER_SIZE);
533 packet.command = SB_COMMAND_CLOSE_SOCKET;
534 packet.u.socket.socket = htobs(socket.GetSocket());
535 packet.u.socket.sequence = socket.GetCloseFlag();
537 Data command(&packet, SB_SOCKET_PACKET_HEADER_SIZE);
538 Data response;
539 try {
540 Send(command, response);
542 catch( Usb::Error & ) {
543 // reset so this won't be called again
544 socket.ForceClosed();
546 eeout(command, response);
547 throw;
550 // starting fresh, reset sequence ID
551 Protocol::CheckSize(response, SB_PACKET_HEADER_SIZE);
552 if( IS_COMMAND(response, SB_COMMAND_SEQUENCE_HANDSHAKE) ) {
553 CheckSequence(0, response);
555 // still need our ACK
556 RawReceive(response);
559 Protocol::CheckSize(response, SB_SOCKET_PACKET_HEADER_SIZE);
560 MAKE_PACKET(rpack, response);
561 if( rpack->command != SB_COMMAND_CLOSED_SOCKET ||
562 btohs(rpack->u.socket.socket) != socket.GetSocket() ||
563 rpack->u.socket.sequence != socket.GetCloseFlag() )
565 // reset so this won't be called again
566 socket.ForceClosed();
568 eout("Packet:\n" << response);
569 throw Error("Socket: Bad CLOSED packet in Close");
572 if( m_resetOnClose ) {
573 Data send, receive;
574 ZeroPacket reset_packet(send, receive);
575 reset_packet.Reset();
577 Send(reset_packet);
578 if( reset_packet.CommandResponse() != SB_COMMAND_RESET_REPLY ) {
579 throw Error("Socket: Missing RESET_REPLY in Close");
583 // // and finally, there always seems to be an extra read of
584 // // an empty packet at the end... just throw it away
585 // try {
586 // RawReceive(response, 1);
587 // }
588 // catch( Usb::Timeout & ) {
589 // }
591 // reset socket and flag
592 socket.ForceClosed();
600 //////////////////////////////////////////////////////////////////////////////
601 // Socket class
603 Socket::Socket( SocketZero &zero,
604 uint16_t socket,
605 uint8_t closeFlag)
606 : m_zero(&zero)
607 , m_socket(socket)
608 , m_closeFlag(closeFlag)
609 , m_registered(false)
613 Socket::~Socket()
615 // trap exceptions in the destructor
616 try {
617 // a non-default socket has been opened, close it
618 Close();
620 catch( std::runtime_error &re ) {
621 // do nothing... log it?
622 dout("Exception caught in ~Socket: " << re.what());
627 ////////////////////////////////////
628 // Socket protected API
630 void Socket::CheckSequence(const Data &seq)
632 m_zero->CheckSequence(m_socket, seq);
635 void Socket::ForceClosed()
637 m_socket = 0;
638 m_closeFlag = 0;
642 ////////////////////////////////////
643 // Socket public API
645 void Socket::Close()
647 UnregisterInterest();
648 m_zero->Close(*this);
653 // Send
655 /// Sends 'send' data to device, no receive.
657 /// \returns void
659 /// \exception Usb::Error on underlying bus errors.
661 void Socket::Send(Data &send, int timeout)
663 // force the socket number to this socket
664 if( send.GetSize() >= SB_PACKET_HEADER_SIZE ) {
665 MAKE_PACKETPTR_BUF(spack, send.GetBuffer());
666 spack->socket = htobs(m_socket);
668 m_zero->RawSend(send, timeout);
672 // Send
674 /// Sends 'send' data to device, and waits for response.
676 /// \returns void
678 /// \exception Usb::Error on underlying bus errors.
680 void Socket::Send(Data &send, Data &receive, int timeout)
682 Send(send, timeout);
683 Receive(receive, timeout);
686 void Socket::Send(Barry::Packet &packet, int timeout)
688 Send(packet.m_send, packet.m_receive, timeout);
691 void Socket::Receive(Data &receive, int timeout)
693 if( m_registered ) {
694 if( m_zero->m_queue ) {
695 if( !m_zero->m_queue->SocketRead(m_socket, receive, timeout) )
696 throw Timeout("Socket::Receive: queue SocketRead returned false (likely a timeout)");
698 else {
699 throw std::logic_error("NULL queue pointer in a registered socket read.");
702 else {
703 m_zero->RawReceive(receive, timeout);
708 // sends the send packet down to the device
709 // Blocks until response received or timed out in Usb::Device
710 void Socket::PacketData(Data &send, Data &receive, int timeout)
712 if( ( send.GetSize() < MIN_PACKET_DATA_SIZE ) ||
713 ( send.GetSize() > MAX_PACKET_DATA_SIZE ) ) {
714 // we don't do that around here
715 throw std::logic_error("Socket: unknown send data in PacketData()");
718 Data &inFrag = receive;
719 receive.Zap();
721 // send non-fragmented
722 Send(send, inFrag, timeout);
724 bool done = false;
725 int blankCount = 0;
727 while( !done ) {
728 // check the packet's validity
729 if( inFrag.GetSize() > 0 ) {
730 MAKE_PACKET(rpack, inFrag);
732 blankCount = 0;
734 Protocol::CheckSize(inFrag, SB_PACKET_HEADER_SIZE);
736 switch( rpack->command )
738 case SB_COMMAND_SEQUENCE_HANDSHAKE:
739 CheckSequence(inFrag);
740 if (m_zero->GetSequencePacket() == false)
741 done = true;
742 break;
744 case SB_COMMAND_JL_READY:
745 case SB_COMMAND_JL_ACK:
746 case SB_COMMAND_JL_HELLO_ACK:
747 case SB_COMMAND_JL_RESET_REQUIRED:
748 done = true;
749 break;
751 case SB_COMMAND_JL_GET_DATA_ENTRY: // This response means that the next packet is the stream
752 done = true;
753 break;
755 case SB_DATA_JL_INVALID: {
756 std::ostringstream oss;
757 oss << "Your Java application has been refused by your device !";
758 eout(oss.str());
759 throw Error(oss.str());
761 break;
763 default:
764 // unknown packet, pass it up to the
765 // next higher code layer
766 done = true;
767 break;
770 else {
771 blankCount++;
772 //std::cerr << "Blank! " << blankCount << std::endl;
773 if( blankCount == 10 ) {
774 // only ask for more data on stalled sockets
775 // for so long
776 throw Error("Socket: 10 blank packets received");
780 if( !done ) {
781 // not done yet, ask for another read
782 Receive(inFrag);
787 // sends the send packet down to the device, fragmenting if
788 // necessary, and returns the response in receive, defragmenting
789 // if needed
790 // Blocks until response received or timed out in Usb::Device
792 // This is primarily for Desktop Database packets... Javaloader
793 // packets use PacketData().
795 void Socket::Packet(Data &send, Data &receive, int timeout)
797 MAKE_PACKET(spack, send);
798 if( send.GetSize() < MIN_PACKET_SIZE ||
799 (spack->command != SB_COMMAND_DB_DATA &&
800 spack->command != SB_COMMAND_DB_DONE) )
802 // we don't do that around here
803 eout("unknown send data in Packet(): " << send);
804 throw std::logic_error("Socket: unknown send data in Packet()");
807 Data inFrag;
808 receive.Zap();
810 if( send.GetSize() <= MAX_PACKET_SIZE ) {
811 // send non-fragmented
812 Send(send, inFrag, timeout);
814 else {
815 // send fragmented
816 unsigned int offset = 0;
817 Data outFrag;
819 do {
820 offset = SocketZero::MakeNextFragment(send, outFrag, offset);
821 Send(outFrag, inFrag, timeout);
823 MAKE_PACKET(rpack, inFrag);
824 // only process sequence handshakes... once we
825 // get to the last fragment, we fall through to normal
826 // processing below
827 if( offset && inFrag.GetSize() > 0 ) {
829 Protocol::CheckSize(inFrag, SB_PACKET_HEADER_SIZE);
831 switch( rpack->command )
833 case SB_COMMAND_SEQUENCE_HANDSHAKE:
834 CheckSequence(inFrag);
835 break;
837 default: {
838 std::ostringstream oss;
839 oss << "Socket: (send) unhandled packet in Packet(): 0x" << std::hex << (unsigned int)rpack->command;
840 eout(oss.str());
841 throw Error(oss.str());
843 break;
848 } while( offset > 0 );
851 bool done = false, frag = false;
852 int blankCount = 0;
853 while( !done ) {
854 MAKE_PACKET(rpack, inFrag);
856 // check the packet's validity
857 if( inFrag.GetSize() > 0 ) {
858 blankCount = 0;
860 Protocol::CheckSize(inFrag, SB_PACKET_HEADER_SIZE);
862 switch( rpack->command )
864 case SB_COMMAND_SEQUENCE_HANDSHAKE:
865 CheckSequence(inFrag);
866 break;
868 case SB_COMMAND_DB_DATA:
869 if( frag ) {
870 SocketZero::AppendFragment(receive, inFrag);
872 else {
873 receive = inFrag;
875 done = true;
876 break;
878 case SB_COMMAND_DB_FRAGMENTED:
879 SocketZero::AppendFragment(receive, inFrag);
880 frag = true;
881 break;
883 case SB_COMMAND_DB_DONE:
884 receive = inFrag;
885 done = true;
886 break;
888 default: {
889 std::ostringstream oss;
890 oss << "Socket: (read) unhandled packet in Packet(): 0x" << std::hex << (unsigned int)rpack->command;
891 eout(oss.str());
892 throw Error(oss.str());
894 break;
897 else {
898 blankCount++;
899 //std::cerr << "Blank! " << blankCount << std::endl;
900 if( blankCount == 10 ) {
901 // only ask for more data on stalled sockets
902 // for so long
903 throw Error("Socket: 10 blank packets received");
907 if( !done ) {
908 // not done yet, ask for another read
909 Receive(inFrag);
914 void Socket::Packet(Barry::Packet &packet, int timeout)
916 Packet(packet.m_send, packet.m_receive, timeout);
919 void Socket::Packet(Barry::JLPacket &packet, int timeout)
921 if( packet.HasData() ) {
922 SetSequencePacket(false);
923 PacketData(packet.m_cmd, packet.m_receive, timeout);
924 SetSequencePacket(true);
925 PacketData(packet.m_data, packet.m_receive, timeout);
927 else {
928 PacketData(packet.m_cmd, packet.m_receive, timeout);
932 void Socket::NextRecord(Data &receive)
934 Barry::Protocol::Packet packet;
935 packet.socket = htobs(GetSocket());
936 packet.size = htobs(7);
937 packet.command = SB_COMMAND_DB_DONE;
938 packet.u.db.tableCmd = 0;
939 packet.u.db.u.command.operation = 0;
941 Data command(&packet, 7);
942 Packet(command, receive);
945 void Socket::RegisterInterest(SocketRoutingQueue::SocketDataHandler handler,
946 void *context)
948 if( !m_zero->m_queue )
949 throw std::logic_error("SocketRoutingQueue required in SocketZero in order to call Socket::RegisterInterest()");
951 if( m_registered )
952 throw std::logic_error("Socket already registered in Socket::RegisterInterest()!");
954 m_zero->m_queue->RegisterInterest(m_socket, handler, context);
955 m_registered = true;
958 void Socket::UnregisterInterest()
960 if( m_registered ) {
961 if( m_zero->m_queue )
962 m_zero->m_queue->UnregisterInterest(m_socket);
963 m_registered = false;
968 } // namespace Barry