4 * Copyright (C) 2001 Barnaby Gray <barnaby@beedesign.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "DirectClient.h"
25 #include "constants.h"
27 #include "sstream_fix.h"
32 using std::ostringstream
;
37 unsigned char DirectClient::client_check_data
[] = {
38 "As part of this software beta version Mirabilis is "
39 "granting a limited access to the ICQ network, "
40 "servers, directories, listings, information and databases (\""
41 "ICQ Services and Information\"). The "
42 "ICQ Service and Information may databases (\""
43 "ICQ Services and Information\"). The "
44 "ICQ Service and Information may\0"
48 * Constructor when receiving an incoming connection
50 DirectClient::DirectClient(ContactRef self
, TCPSocket
*sock
, MessageHandler
*mh
,
51 ContactTree
*cl
, unsigned int ext_ip
, unsigned short server_port
)
52 : m_state(WAITING_FOR_INIT
), m_recv(),
53 m_self_contact(self
), m_contact(NULL
), m_contact_list(cl
),
54 m_message_handler(mh
), m_incoming(true), m_local_ext_ip(ext_ip
),
55 m_local_server_port(server_port
)
62 * Constructor for making an outgoing connection
64 DirectClient::DirectClient(ContactRef self
, ContactRef c
, MessageHandler
*mh
, unsigned int ext_ip
,
65 unsigned short server_port
)
66 : m_state(NOT_CONNECTED
), m_recv(), m_self_contact(self
),
67 m_contact(c
), m_message_handler(mh
), m_incoming(false), m_local_ext_ip(ext_ip
),
68 m_local_server_port(server_port
)
72 m_socket
= new TCPSocket();
73 m_remote_uin
= c
->getUIN();
76 DirectClient::~DirectClient()
78 m_msgcache
.expireAll();
80 while (!m_msgqueue
.empty()) {
81 expired_cb( m_msgqueue
.front() );
82 m_msgqueue
.pop_front();
85 if ( m_socket
->getSocketHandle() > -1) SignalRemoveSocket( m_socket
->getSocketHandle() );
89 void DirectClient::Init()
92 m_msgcache
.setDefaultTimeout(30);
93 m_msgcache
.expired
.connect( this, &DirectClient::expired_cb
) ;
96 void DirectClient::Connect() {
97 m_remote_tcp_version
= m_contact
->getTCPVersion();
98 if (m_remote_tcp_version
>= 7) m_eff_tcp_version
= 7;
99 else if (m_remote_tcp_version
== 6) m_eff_tcp_version
= 6;
100 else throw DisconnectedException("Cannot direct connect to client with too old TCP version");
102 m_socket
->setRemoteIP( m_contact
->getLanIP() );
103 m_socket
->setRemotePort( m_contact
->getLanPort() );
104 m_socket
->setBlocking(false);
106 SignalAddSocket( m_socket
->getSocketHandle(), SocketEvent::WRITE
);
108 if (m_contact
->getDCCookie() != 0)
110 m_session_id
= m_contact
->getDCCookie();
114 m_session_id
= (unsigned int)(0xffffffff*(rand()/(RAND_MAX
+1.0)));
118 m_state
= WAITING_FOR_INIT_ACK
;
121 void DirectClient::FinishNonBlockingConnect()
126 void DirectClient::clearoutMessagesPoll()
128 m_msgcache
.clearoutPoll();
131 void DirectClient::expired_cb(MessageEvent
*ev
) {
132 ev
->setFinished(false);
133 ev
->setDelivered(false);
138 void DirectClient::Recv() {
140 while ( m_socket
->connected() ) {
141 if ( !m_socket
->Recv(m_recv
) ) break;
144 } catch(SocketException e
) {
146 ostr
<< "Failed on recv: " << e
.what();
147 throw DisconnectedException( ostr
.str() );
148 } catch(ParseException e
) {
150 ostr
<< "Failed parsing: " << e
.what();
151 throw DisconnectedException( ostr
.str() );
155 void DirectClient::Parse() {
156 if (m_recv
.empty()) return;
160 while (!m_recv
.empty()) {
163 m_recv
.setLittleEndian();
165 if (length
== 0) return; // short read, toss it back (nothing to do)
166 if (length
> Incoming_Packet_Limit
) throw ParseException("Received too long incoming packet");
167 if (m_recv
.remains() < length
) return; // waiting for more of the packet
171 m_recv
.chopOffBuffer( sb
, length
+2 );
174 ostr
<< "Received packet from " << IPtoString( m_socket
->getRemoteIP() ) << ":" << m_socket
->getRemotePort() << endl
<< sb
;
175 SignalLog(LogEvent::DIRECTPACKET
, ostr
.str());
177 if (m_state
== WAITING_FOR_INIT
) {
183 m_state
= WAITING_FOR_INIT_ACK
;
186 if (m_eff_tcp_version
== 7) {
188 m_state
= WAITING_FOR_INIT2
;
192 connected
.emit(this);
196 } else if (m_state
== WAITING_FOR_INIT_ACK
) {
201 if (m_eff_tcp_version
== 7)
202 m_state
= WAITING_FOR_INIT2
; // v7 has an extra stage of handshaking
205 m_state
= CONNECTED
; // v5 is done handshaking now
207 connected
.emit(this);
211 // Outgoing - next packet should be their INIT
212 m_state
= WAITING_FOR_INIT
;
215 } else if (m_state
== WAITING_FOR_INIT2
) {
217 // This is a V7 only packet
226 connected
.emit(this);
228 } else if (m_state
== CONNECTED
) {
233 if (sb
.beforeEnd()) {
234 /* we assert that parsing code eats uses all data
235 * in the FLAP - seems useful to know when they aren't
236 * as it probably means they are faulty
239 ostr
<< "Buffer pointer not at end after parsing packet was: 0x" << std::hex
<< sb
.pos()
240 << " should be: 0x" << sb
.size();
241 SignalLog(LogEvent::WARN
, ostr
.str());
248 void DirectClient::ConfirmUIN() {
249 if ( m_contact_list
->exists(m_remote_uin
) ) {
250 ContactRef c
= (*m_contact_list
)[ m_remote_uin
];
251 if ( (c
->getExtIP() == m_local_ext_ip
&& c
->getLanIP() == getIP() )
252 /* They are behind the same masquerading box,
253 * and the Lan IP matches
255 || c
->getExtIP() == getIP()) {
258 // spoofing attempt most likely
260 ostr
<< "Refusing direct connection from someone that claims to be UIN "
261 << m_remote_uin
<< " since their IP " << IPtoString( getIP() ) << " != " << IPtoString( c
->getExtIP() );
262 throw DisconnectedException( ostr
.str() );
266 // don't accept direct connections from contacts not on contact list
267 throw DisconnectedException("Refusing direct connection to contact not on contact list");
271 void DirectClient::SendInitPacket() {
274 Buffer::marker m1
= b
.getAutoSizeShortMarker();
276 b
<< (unsigned char)0xff; // start byte
277 b
<< (unsigned short)0x0007; // tcp version
278 Buffer::marker m2
= b
.getAutoSizeShortMarker();
281 b
<< (unsigned short)0x0000;
282 b
<< (unsigned int)m_local_server_port
;
284 b
<< m_self_contact
->getUIN();
287 b
<< m_socket
->getLocalIP();
288 b
<< (unsigned char)0x04; // mode
290 b
<< (unsigned int)m_local_server_port
;
293 b
<< (unsigned int)0x00000050; // unknown
294 b
<< (unsigned int)0x00000003; // unknown
295 if (m_eff_tcp_version
== 7)
296 b
<< (unsigned int)0x00000000; // unknown
298 b
.setAutoSizeMarker(m1
);
299 b
.setAutoSizeMarker(m2
);
304 void DirectClient::ParseInitPacket(Buffer
&b
) {
306 unsigned short length
;
309 unsigned char start_byte
;
311 if (start_byte
!= 0xff) throw ParseException("Init Packet didn't start with 0xff");
313 unsigned short tcp_version
;
315 b
.advance(2); // revision or a length ??
318 m_remote_tcp_version
= tcp_version
;
319 if (tcp_version
<= 5) throw ParseException("Too old client < ICQ99");
320 if (tcp_version
== 6) m_eff_tcp_version
= 6;
321 else m_eff_tcp_version
= 7;
323 if (tcp_version
!= m_remote_tcp_version
) throw ParseException("Client claiming different TCP versions");
326 unsigned int our_uin
;
328 if (our_uin
!= m_self_contact
->getUIN()) throw ParseException("Local UIN in Init Packet not same as our Local UIN");
331 // xx xx senders open port
335 unsigned int remote_uin
;
338 m_remote_uin
= remote_uin
;
340 if (m_remote_uin
!= remote_uin
) throw ParseException("Remote UIN in Init Packet for Remote Client not what was expected");
343 // xx xx xx xx senders external IP
344 // xx xx xx xx senders lan IP
349 // xx xx senders port again
353 // xx xx xx xx session id
354 unsigned int session_id
;
357 m_session_id
= session_id
;
359 if (m_session_id
!= session_id
) throw ParseException("Session ID from Remote Client doesn't match the one we sent");
362 // 50 00 00 00 unknown
363 // 03 00 00 00 unknown
366 if (m_eff_tcp_version
== 7) {
367 b
.advance(4); // 00 00 00 00 unknown
372 void DirectClient::ParseInitAck(Buffer
&b
) {
374 unsigned short length
;
376 if (length
!= 4) throw ParseException("Init Ack not as expected");
379 b
>> a
; // should be 0x00000001 really
382 void DirectClient::ParseInit2(Buffer
&b
) {
384 unsigned short length
;
386 if (length
!= 0x0021) throw ParseException("V7 final handshake packet incorrect length");
390 if (type
!= 0x03) throw ParseException("Expecting V7 final handshake packet, received something else");
392 unsigned int unknown
;
393 b
>> unknown
// 0x0000000a
395 b
.advance(24); // unknowns
398 void DirectClient::SendInit2() {
401 Buffer::marker m1
= b
.getAutoSizeShortMarker();
402 b
<< (unsigned char) 0x03 // start byte
403 << (unsigned int) 0x0000000a // unknown
404 << (unsigned int) 0x00000001 // unknown
405 << (unsigned int) (m_incoming
? 0x00000001 : 0x00000000) // unknown
406 << (unsigned int) 0x00000000 // unknown
407 << (unsigned int) 0x00000000; // unknown
409 b
<< (unsigned int) 0x00040001 // unknown
410 << (unsigned int) 0x00000000 // unknown
411 << (unsigned int) 0x00000000; // unknown
413 b
<< (unsigned int) 0x00000000 // unknown
414 << (unsigned int) 0x00000000 // unknown
415 << (unsigned int) 0x00040001; // unknown
417 b
.setAutoSizeMarker(m1
);
421 void DirectClient::ParsePacket(Buffer
& b
) {
423 if (!Decrypt(b
, c
)) throw ParseException("Decrypting failed");
427 void DirectClient::ParsePacketInt(Buffer
& b
) {
429 unsigned short length
;
432 // we should get the decrypted packet in
433 unsigned int checksum
;
434 unsigned short command
, seqnum
, unknown
, version
;
439 if (m_eff_tcp_version
== 7) {
440 unsigned char start_byte
;
442 if (start_byte
!= 0x02) throw ParseException("Message Packet didn't start with 0x02");
447 >> unknown
// 0x000e or 0x0012 in probe packets
450 if (unknown
!= 0x000e) throw ParseException("Ignoring weird packet");
452 b
.advance(12); // unknown 3 ints
454 ICQSubType
*i
= ICQSubType::ParseICQSubType(b
, true, (command
== V6_TCP_ACK
));
455 if (dynamic_cast<UINICQSubType
*>(i
) == NULL
) throw ParseException("Unknown ICQ subtype");
456 UINICQSubType
*icqsubtype
= dynamic_cast<UINICQSubType
*>(i
);
458 icqsubtype
->setSeqNum(seqnum
);
459 icqsubtype
->setSource( m_contact
->getUIN() );
461 if (command
== 0) throw ParseException("Invalid TCP Packet");
467 if (icqsubtype
->getType() == MSG_Type_FT
)
469 FTICQSubType
*fticqsubtype
= static_cast<FTICQSubType
*>( icqsubtype
);
470 FileTransferEvent
*ev
= m_message_handler
->handleIncomingFT( fticqsubtype
, true );
471 m_msgcache
.insert( seqnum
, ev
);
475 bool ack
= m_message_handler
->handleIncoming( icqsubtype
);
476 if (ack
) SendPacketAck(icqsubtype
);
481 if ( m_msgcache
.exists(seqnum
) )
483 MessageEvent
*ev
= m_msgcache
[seqnum
];
485 m_message_handler
->handleIncomingACK( ev
, icqsubtype
);
486 m_msgcache
.remove(seqnum
);
487 if (icqsubtype
->getType() != MSG_Type_FT
)
488 delete ev
; //WARNING!!! FileTransferEvent shall not be deleted here?
493 SignalLog(LogEvent::WARN
, "Received Direct ACK for unknown message");
498 // FileTransfer was cancelled before we responded
499 if ( icqsubtype
->getType() == MSG_Type_FT
500 && m_msgcache
.exists(seqnum
) )
502 MessageEvent
*ev
= m_msgcache
[seqnum
];
503 if ( ev
->getType() == MessageEvent::FileTransfer
)
505 FileTransferEvent
*eev
= static_cast<FileTransferEvent
*>(ev
);
506 m_message_handler
->handleIncomingFTCancel( eev
);
507 m_msgcache
.remove(seqnum
);
513 SignalLog(LogEvent::WARN
, "Received Direct Cancel for unknown message");
517 ostr
<< "Unknown TCP Command received 0x" << command
;
518 throw ParseException( ostr
.str() );
527 bool DirectClient::Decrypt(Buffer
& in
, Buffer
& out
) {
529 if (m_eff_tcp_version
>= 6) {
530 // Huge *thanks* to licq for this code
532 unsigned long hex
, key
, B1
, M1
;
534 unsigned char X1
, X2
, X3
;
535 unsigned int correction
;
537 if (m_eff_tcp_version
== 7) correction
= 3;
540 unsigned int size
= in
.size()-correction
;
542 in
.setLittleEndian();
543 out
.setLittleEndian();
545 unsigned short length
;
549 if (m_eff_tcp_version
== 7) {
550 unsigned char start_byte
;
560 key
= 0x67657268 * size
+ check
;
562 for(i
=4; i
<(size
+3)/4; i
+=4) {
563 hex
= key
+ client_check_data
[i
&0xFF];
565 out
<< (unsigned char)(in
.UnpackChar() ^ (hex
&0xFF));
566 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>8)&0xFF));
567 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>16)&0xFF));
568 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>24)&0xFF));
572 while (in
.remains()) {
577 B1
= (out
[4+correction
]<<24) | (out
[6+correction
]<<16) | (out
[4+correction
]<<8) | (out
[6+correction
]<<0);
579 // special decryption
583 M1
= (B1
>> 24) & 0xFF;
584 if(M1
< 10 || M1
>= size
) return false;
586 X1
= out
[M1
+correction
] ^ 0xFF;
587 if(((B1
>> 16) & 0xFF) != X1
) return false;
589 X2
= ((B1
>> 8) & 0xFF);
591 X3
= client_check_data
[X2
] ^ 0xFF;
592 if((B1
& 0xFF) != X3
) return false;
597 ostr
<< "Decrypted Direct packet from " << IPtoString( m_socket
->getRemoteIP() ) << ":" << m_socket
->getRemotePort() << endl
<< out
;
598 SignalLog(LogEvent::DIRECTPACKET
, ostr
.str());
603 void DirectClient::Encrypt(Buffer
& in
, Buffer
& out
) {
606 ostr
<< "Unencrypted packet to " << IPtoString( m_socket
->getRemoteIP() ) << ":" << m_socket
->getRemotePort() << endl
<< in
;
607 SignalLog(LogEvent::DIRECTPACKET
, ostr
.str());
609 if (m_eff_tcp_version
== 6 || m_eff_tcp_version
== 7) {
610 // Huge *thanks* to licq for this code
612 unsigned long hex
, key
, B1
, M1
;
613 unsigned int i
, check
;
614 unsigned char X1
, X2
, X3
;
615 unsigned int size
= in
.size();
617 in
.setLittleEndian();
618 out
.setLittleEndian();
620 if (m_eff_tcp_version
== 7) {
621 // correction for next byte
622 out
<< (unsigned short)(size
+ 1);
623 out
<< (unsigned char)0x02;
625 out
<< (unsigned short)size
;
628 // calculate verification data
629 M1
= (rand() % ((size
< 255 ? size
: 255)-10))+10;
632 X3
= client_check_data
[X2
] ^ 0xFF;
634 B1
= (in
[4]<<24)|(in
[6]<<16)|(in
[4]<<8)|(in
[6]);
636 // calculate checkcode
637 check
= (M1
<< 24) | (X1
<< 16) | (X2
<< 8) | X3
;
643 key
= 0x67657268 * size
+ check
;
645 // XORing the actual data
647 for(i
=4;i
<(size
+3)/4;i
+=4){
648 hex
= key
+ client_check_data
[i
&0xFF];
650 out
<< (unsigned char)(in
.UnpackChar() ^ (hex
&0xFF));
651 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>8)&0xFF));
652 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>16)&0xFF));
653 out
<< (unsigned char)(in
.UnpackChar() ^ ((hex
>>24)&0xFF));
657 while (in
.remains()) {
666 void DirectClient::SendInitAck()
670 Buffer::marker m1
= b
.getAutoSizeShortMarker();
671 b
<< (unsigned int)0x00000001;
672 b
.setAutoSizeMarker(m1
);
676 void DirectClient::SendPacketAck(ICQSubType
*icqsubtype
)
681 b
<< (unsigned int)0x00000000 // checksum (filled in by Encrypt)
683 << (unsigned short)0x000e
684 << icqsubtype
->getSeqNum()
685 << (unsigned int)0x00000000
686 << (unsigned int)0x00000000
687 << (unsigned int)0x00000000;
689 icqsubtype
->Output(b
);
695 void DirectClient::SendPacketEvent(MessageEvent
*ev
)
699 unsigned short seqnum
= NextSeqNum();
701 UINICQSubType
*ist
= m_message_handler
->handleOutgoing(ev
);
702 if (ist
== NULL
) return;
704 ist
->setAdvanced(true);
707 b
<< (unsigned int)0x00000000 // checksum (filled in by Encrypt)
709 << (unsigned short)0x000e
711 << (unsigned int)0x00000000
712 << (unsigned int)0x00000000
713 << (unsigned int)0x00000000;
720 // Save seqnum so later ACK or CANCEL could be sent.
721 if (ist
->getType() == MSG_Type_FT
)
723 FileTransferEvent
*fev
= static_cast<FileTransferEvent
*>(ev
);
724 fev
->setSeqNum(seqnum
);
727 m_msgcache
.insert(seqnum
, ev
);
732 void DirectClient::Send(Buffer
&b
) {
735 ostr
<< "Sending packet to " << IPtoString( m_socket
->getRemoteIP() ) << ":" << m_socket
->getRemotePort() << endl
<< b
;
736 SignalLog(LogEvent::DIRECTPACKET
, ostr
.str());
738 } catch(SocketException e
) {
740 ostr
<< "Failed to send: " << e
.what();
741 throw DisconnectedException( ostr
.str() );
746 void DirectClient::SendFTACK(FileTransferEvent
*ev
)
748 FTICQSubType icqsubtype
;
749 icqsubtype
.setAdvanced(true);
750 icqsubtype
.setSeqNum(ev
->getSeqNum());
751 icqsubtype
.setACK(true);
752 if (ev
->getState() == FileTransferEvent::ACCEPTED
)
754 icqsubtype
.setPort(ev
->getPort());
755 icqsubtype
.setRevPort(ev
->getPort());
756 icqsubtype
.setSize(0);
757 icqsubtype
.setMessage("");
759 // port numbers, etc..
763 icqsubtype
.setStatus( AcceptStatus_Denied
);
764 icqsubtype
.setPort(0);
765 icqsubtype
.setSize(0);
766 icqsubtype
.setMessage( ev
->getRefusalMessage() );
768 SendPacketAck(&icqsubtype
);
769 while (m_msgcache
.exists(ev
->getSeqNum()))
771 m_msgcache
.remove(ev
->getSeqNum());
775 void DirectClient::SendFTCancel(FileTransferEvent
*ev
)
778 fist
.setAdvanced(true);
779 fist
.setSeqNum(ev
->getSeqNum());
783 fist
.setMessage( "" );
787 b
<< (unsigned int)0x00000000 // checksum (filled in by Encrypt)
789 << (unsigned short)0x000e
791 << (unsigned int)0x00000000
792 << (unsigned int)0x00000000
793 << (unsigned int)0x00000000;
800 m_msgcache
.remove(ev
->getSeqNum());
803 void DirectClient::SendEvent(MessageEvent
*ev
)
806 if (m_state
== CONNECTED
) {
807 // send straight away
811 m_msgqueue
.push_back(ev
);
816 void DirectClient::flush_queue() {
817 while (!m_msgqueue
.empty()) {
818 SendPacketEvent( m_msgqueue
.front() );
819 m_msgqueue
.pop_front();
823 unsigned short DirectClient::NextSeqNum() {
827 unsigned int DirectClient::getUIN() const { return m_remote_uin
; }
829 unsigned int DirectClient::getIP() const { return m_socket
->getRemoteIP(); }
831 unsigned short DirectClient::getPort() const { return m_socket
->getRemotePort(); }
833 int DirectClient::getfd() const { return m_socket
->getSocketHandle(); }
835 TCPSocket
* DirectClient::getSocket() const { return m_socket
; }
837 void DirectClient::setContact(ContactRef c
) { m_contact
= c
; }
839 ContactRef
DirectClient::getContact() const { return m_contact
; }