4 #import <CommonCrypto/CommonDigest.h>
8 @synthesize tagName = m_name;
9 @synthesize tagType = m_type;
10 @synthesize subtags = m_subtags;
12 + (id)tagFromBuffer:(uint8_t **) buffer withLenght:(int) length {
13 ECTag *tag = nil; //[[ECTag alloc] init];
16 NSLog(@"[EC] buffer for tag is too short");
20 uint16_t name16 = *(uint16_t *)(*buffer);
21 name16 = ntohs(name16);
22 bool have_subtags = (name16 & 1) != 0;
24 ECTagNames tag_name = (ECTagNames)(name16);
25 *buffer += sizeof(name16);
27 uint8_t type8 = *(*buffer);
28 ECTagTypes tag_type = (ECTagTypes)type8;
29 *buffer += sizeof(type8);
31 uint32_t size32 = *(uint32_t *)(*buffer);
32 size32 = ntohl(size32);
33 *buffer += sizeof(uint32_t);
35 NSMutableArray *subtags = have_subtags ? [ECTag readSubtags:buffer withLenght:(length-3)] : nil;
38 case EC_TAGTYPE_UINT8:
39 tag = [ECTagInt8 tagFromBuffer:buffer];
41 case EC_TAGTYPE_UINT16:
42 tag = [ECTagInt16 tagFromBuffer:buffer];
44 case EC_TAGTYPE_UINT32:
45 tag = [ECTagInt32 tagFromBuffer:buffer];
47 case EC_TAGTYPE_UINT64:
48 tag = [ECTagInt64 tagFromBuffer:buffer];
50 case EC_TAGTYPE_HASH16:
51 tag = [ECTagMD5 tagFromBuffer:buffer];
53 case EC_TAGTYPE_STRING:
54 tag = [ECTagString tagFromBuffer:buffer];
60 tag->m_name = tag_name;
61 tag->m_subtags = subtags;
66 + (NSMutableArray *)readSubtags:(uint8_t **) buffer withLenght:(int) length {
68 uint16_t count16 = *(uint16_t *)(*buffer);
69 count16 = ntohs(count16);
70 *buffer += sizeof(count16);
71 NSMutableArray *array = [[NSMutableArray alloc] init];
73 for(int i = 0; i < count16; i++) {
74 id tag = [ECTag tagFromBuffer:buffer withLenght:length];
76 [array addObject:tag];
83 - (void)writeToSocket:(NSOutputStream *) socket {
84 uint16_t name16 = (uint16_t)m_name;
86 uint8_t type8 = (uint8_t)m_type;
87 if ( [m_subtags count] ) {
90 name16 = htons(name16);
91 [socket write:(uint8_t *)&name16 maxLength:sizeof(name16)];
92 [socket write:&type8 maxLength:sizeof(type8)];
93 uint32_t size32 = [self getSize];
94 size32 = htonl(size32);
95 [socket write:(uint8_t *)&size32 maxLength:sizeof(size32)];
98 - (void)writeSubtagsToSocket:(NSOutputStream *) socket {
99 uint16_t count16 = [m_subtags count];
100 count16 = htons(count16);
101 [socket write:(uint8_t *)&count16 maxLength:sizeof(count16)];
102 for (ECTag *t in m_subtags) {
103 [t writeToSocket:socket];
108 int total_size = m_size;
109 for (ECTag *t in m_subtags) {
110 total_size += [t getSize];
111 // name + type + size
112 total_size += (2 + 1 + 4);
113 if ([t->m_subtags count]) {
120 - (id)tagByName:(ECTagNames) tagname {
122 for (ECTag *t in m_subtags) {
123 if (t.tagName == tagname) {
131 - (void)initSubtags {
132 m_subtags = [NSMutableArray array];
141 - (uint64_t)tagInt64ByName: (ECTagNames) tagname {
142 ECTag *st = [self tagByName: tagname];
147 switch ([st getSize]) {
149 ECTagInt8 *t = (ECTagInt8 *)st;
150 value = t.uint8Value;
154 ECTagInt16 *t = (ECTagInt16 *)st;
155 value = t.uint16Value;
159 ECTagInt32 *t = (ECTagInt32 *)st;
160 value = t.uint32Value;
164 ECTagInt64 *t = (ECTagInt64 *)st;
165 value = t.uint64Value;
173 return [m_subtags count];
178 @implementation ECTagInt8
180 @synthesize uint8Value = m_val;
182 + (id)tagFromInt8:(uint8_t) value withName:(ECTagNames) name {
183 ECTagInt8 *tag = [[ECTagInt8 alloc] init];
186 tag->m_type = EC_TAGTYPE_UINT8;
192 + (id)tagFromBuffer:(uint8_t **) buffer {
193 ECTagInt8 *tag = [[ECTagInt8 alloc] init];
194 tag->m_val = **buffer;
196 tag->m_type = EC_TAGTYPE_UINT8;
203 - (void)writeToSocket:(NSOutputStream *) socket {
204 [super writeToSocket:socket];
206 [socket write:&m_val maxLength:sizeof(m_val)];
211 @implementation ECTagInt16
213 @synthesize uint16Value = m_val;
215 + (id)tagFromInt16:(uint16_t) value withName:(ECTagNames) name {
216 ECTagInt16 *tag = [[ECTagInt16 alloc] init];
219 tag->m_type = EC_TAGTYPE_UINT16;
225 + (id)tagFromBuffer:(uint8_t **) buffer {
226 ECTagInt16 *tag = [[ECTagInt16 alloc] init];
228 tag->m_val = ntohs(*((uint16_t *)(*buffer)));
230 tag->m_type = EC_TAGTYPE_UINT16;
240 @implementation ECTagInt32
242 @synthesize uint32Value = m_val;
244 + (id)tagFromInt32:(uint32_t) value withName:(ECTagNames) name {
245 ECTagInt32 *tag = [[ECTagInt32 alloc] init];
248 tag->m_type = EC_TAGTYPE_UINT32;
254 + (id)tagFromBuffer:(uint8_t **) buffer {
255 ECTagInt32 *tag = [[ECTagInt32 alloc] init];
257 tag->m_val = ntohl(*((uint32_t *)(*buffer)));
259 tag->m_type = EC_TAGTYPE_UINT32;
269 @implementation ECTagInt64
271 @synthesize uint64Value = m_val;
273 + (id)tagFromInt64:(uint64_t) value withName:(ECTagNames) name {
274 ECTagInt64 *tag = [[ECTagInt64 alloc] init];
277 tag->m_type = EC_TAGTYPE_UINT64;
284 + (id)tagFromBuffer:(uint8_t **) buffer {
285 ECTagInt64 *tag = [[ECTagInt64 alloc] init];
288 uint32 val32 = *((uint32_t *)(*buffer));
292 val32 = *((uint32_t *)(*buffer));
296 tag->m_val = (hi << 32) | lo;
298 tag->m_type = EC_TAGTYPE_UINT64;
304 - (void)writeToSocket:(NSOutputStream *) socket {
305 [super writeToSocket:socket];
307 uint32_t val32 = m_val >> 32;
308 val32 = htonl(val32);
309 [socket write:(uint8_t *)&val32 maxLength:sizeof(val32)];
311 val32 = m_val & 0xffffffff;
312 val32 = htonl(val32);
313 [socket write:(uint8_t *)&val32 maxLength:sizeof(val32)];
319 @implementation ECTagData
321 - (void)writeToSocket:(NSOutputStream *) socket {
322 [super writeToSocket:socket];
324 [socket write:(const uint8_t *)[m_data bytes] maxLength:m_size];
332 + (id)tagFromBuffer:(uint8_t **) buffer withLenght:(int) length {
333 ECTagData *tag = [[ECTagData alloc] init];
335 tag->m_data = [NSData dataWithBytes: *buffer length: length];
336 [tag->m_data retain];
344 @implementation ECTagMD5
346 + (id)tagFromString:(NSString *) string withName:(ECTagNames) name {
347 ECTagMD5 *tag = [[ECTagMD5 alloc] init];
350 unsigned char md5data[16];
352 CC_MD5_Update(&ctx, [string UTF8String], [string length]);
353 CC_MD5_Final(md5data, &ctx);
355 tag->m_data = [NSData dataWithBytes: md5data length: sizeof(md5data)];
356 [tag->m_data retain];
358 tag->m_type = EC_TAGTYPE_HASH16;
364 + (id)tagFromBuffer:(uint8_t **) buffer {
365 ECTagMD5 *tag = [[ECTagMD5 alloc] init];
368 tag->m_val.lo = *((uint64_t *)(*buffer));
370 tag->m_val.hi = *((uint64_t *)(*buffer));
376 - (MD5Data)getMD5Data {
378 uint8_t *data_ptr = (uint8_t *)[m_data bytes];
380 uint64_t hi = *(uint64_t *)data_ptr;
382 uint64_t lo = *(uint64_t *)data_ptr;
383 MD5Data md5 = {hi, lo};
389 - (NSString *)stringKey {
390 NSString *s = [NSString stringWithFormat:@"%qx%qx", m_val.hi, m_val.lo];
396 @implementation ECTagString
398 @synthesize stringValue = m_val;
400 + tagFromString:(NSString *) string withName:(ECTagNames) name {
401 ECTagString *tag = [[ECTagString alloc] init];
403 const char *rawstring = [string UTF8String];
404 tag->m_size = strlen(rawstring) + 1;
405 tag->m_data = [NSData dataWithBytes: rawstring length: tag->m_size];
406 [tag->m_data retain];
407 tag->m_type = EC_TAGTYPE_STRING;
413 + (id)tagFromBuffer:(uint8_t **) buffer {
414 ECTagString *tag = [[ECTagString alloc] init];
416 tag->m_val = [NSString stringWithCString:(char *)(*buffer) encoding:NSUTF8StringEncoding];
417 *buffer += [tag->m_val length] + 1;
424 @implementation ECPacket
426 @synthesize opcode = m_opcode;
428 + (id)packetWithOpcode:(ec_opcode_t) opcode {
429 ECPacket *p = [[ECPacket alloc] init];
431 [p initWithOpcode:opcode];
437 - (void)initWithOpcode:(ec_opcode_t) opcode {
441 // allow notification push to my client
442 m_flags |= EC_FLAG_NOTIFY | EC_FLAG_ACCEPTS;
447 + (id)packetFromBuffer:(uint8_t *) buffer withLength:(int)length {
451 ECPacket *p = [[ECPacket alloc] init];
452 uint8_t *data = buffer;
454 p->m_flags = ntohl(*((uint32_t *)data));
456 uint32_t packet_size = ntohl(*((uint32_t *)data));
458 if ( packet_size > 1024*1024 ) {
461 p->m_opcode = (ec_opcode_t)(*data);
464 uint16_t tag_count = ntohs(*((uint16_t *)data));
467 p->m_subtags = [[NSMutableArray alloc] init];
468 [p->m_subtags retain];
469 uint8_t *start_ptr = data;
470 for(int i = 0; i < tag_count; i++) {
471 ECTag *tag = [ECTag tagFromBuffer:&data withLenght:(length - (data - start_ptr))];
472 // some tags are not supported yet
474 [p->m_subtags addObject:tag];
482 - (void)writeToSocket:(NSOutputStream *) socket {
483 // 1 (command) + 2 (tag count)
484 int packet_size = [self getSize] + 1 + 2;
486 // No need for zlib on client side
487 // if ( packet_size > MaxUncompressedPacket ) {
488 // m_flags |= (Int32)ECFlags.EC_FLAG_ZLIB;
490 uint32_t val32 = htonl(m_flags);
491 [socket write:(uint8_t *)&val32 maxLength:sizeof(val32)];
492 if ( m_flags & EC_FLAG_ACCEPTS ) {
493 [socket write:(uint8_t *)&val32 maxLength:sizeof(val32)];
495 val32 = htonl(packet_size);
496 [socket write:(uint8_t *)&val32 maxLength:sizeof(val32)];
497 [socket write:(uint8_t *)&m_opcode maxLength:sizeof(m_opcode)];
499 if ( [m_subtags count] ) {
500 [self writeSubtagsToSocket:socket];
503 [socket write:(uint8_t *)&val16 maxLength:sizeof(val16)];
509 @implementation ECLoginAuthPacket
511 - (NSString *)getMD5_Str:(NSString *) str {
513 unsigned char md5data[16];
515 CC_MD5_Update(&ctx, [str UTF8String], [str length]);
516 CC_MD5_Final(md5data, &ctx);
517 NSString *MD5str = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
518 md5data[0], md5data[1],md5data[2],md5data[3],
519 md5data[4],md5data[5],md5data[6],md5data[7],
520 md5data[8],md5data[9],md5data[10],md5data[11],
521 md5data[12],md5data[13],md5data[14],md5data[15]
526 + (id)loginPacket:(NSString *) password withSalt:(uint64_t) salt {
527 ECLoginAuthPacket *p = [[ECLoginAuthPacket alloc] init];
529 [p initWithOpcode:EC_OP_AUTH_PASSWD];
531 NSString *saltStr = [NSString stringWithFormat:@"%llX", salt];
533 // unsigned char md5data[16];
534 // CC_MD5_Init(&ctx);
535 // CC_MD5_Update(&ctx, [saltStr UTF8String], [saltStr length]);
536 // CC_MD5_Final(md5data, &ctx);
537 // NSString *saltMD5 = [NSString stringWithFormat:@"%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x",
538 // md5data[0], md5data[1],md5data[2],md5data[3],
539 // md5data[4],md5data[5],md5data[6],md5data[7],
540 // md5data[8],md5data[9],md5data[10],md5data[11],
541 // md5data[12],md5data[13],md5data[14],md5data[15]
543 NSString *saltMD5 = [p getMD5_Str: saltStr];
545 NSString *newPass = [NSString stringWithFormat:@"%@%@", [p getMD5_Str: password], saltMD5];
547 NSLog(@"[EC] using salt=%@ saltHash=%@ newPass=%@\n", saltStr, saltMD5, newPass);
549 ECTagMD5 *passtag = [ECTagMD5 tagFromString: newPass withName:EC_TAG_PASSWD_HASH];
550 [p->m_subtags addObject:passtag];
558 @implementation ECLoginRequestPacket
560 + (id)loginPacket:(NSString *) version {
561 ECLoginRequestPacket *p = [[ECLoginRequestPacket alloc] init];
563 [p initWithOpcode:EC_OP_AUTH_REQ];
565 ECTagString *version_tag = [ECTagString tagFromString:version withName:EC_TAG_CLIENT_VERSION];
566 [p->m_subtags addObject:version_tag];
568 ECTagString *client_name_tag = [ECTagString tagFromString:@"cocoa-frontend" withName:EC_TAG_CLIENT_NAME];
569 [p->m_subtags addObject:client_name_tag];
571 ECTagInt64 *proto_version_tag = [ECTagInt64 tagFromInt64:EC_CURRENT_PROTOCOL_VERSION withName:EC_TAG_PROTOCOL_VERSION];
572 [p->m_subtags addObject:proto_version_tag];
581 @implementation ECRemoteConnection
583 @synthesize error = m_error;
585 + (id)remoteConnection {
586 ECRemoteConnection *p = [[ECRemoteConnection alloc] init];
589 // rx buffer can be resized as needed
591 p->m_rxbuf = [NSMutableData dataWithLength:1024];
594 p->m_login_handler = [amuleLoginHandler initWithConnection:p];
595 [p->m_login_handler retain];
597 // client only transmit commands, which are
598 // quite small in size. "big enough" buffer will do the trick
600 // p->m_txbuf = [NSMutableData dataWithLength:1024];
601 // [p->m_txbuf retain];
617 - (bool)isIpv4Address:(NSString *) address {
618 NSArray *ar = [address componentsSeparatedByString:@"."];
619 if ( [ar count] != 4 ) {
622 for (NSString *s in ar) {
623 const char *p = [s UTF8String];
625 if ( !isdigit(*p) ) {
634 - (void)connectToAddress:(NSString *) hostname withPort:(int)trgport {
637 NSHost *host = [NSHost hostWithName:hostname];
638 NSString *addr = nil;
641 // On Mac localhost has ipv6 address (linklocal), but amuled listen
644 for (NSString *ad in host.addresses) {
645 NSLog(@"host have address=%@ is_ipv4=%d\n", ad, [self isIpv4Address:ad]);
646 if ( [self isIpv4Address:ad] ) {
654 host = [NSHost hostWithAddress:addr];
656 [NSStream getStreamsToHost:host port:trgport inputStream:&m_istream outputStream:&m_ostream];
661 [m_istream setDelegate:self];
662 [m_ostream setDelegate:self];
664 [m_istream scheduleInRunLoop:[NSRunLoop currentRunLoop]
665 forMode:NSDefaultRunLoopMode];
667 [m_ostream scheduleInRunLoop:[NSRunLoop currentRunLoop]
668 forMode:NSDefaultRunLoopMode];
673 m_remaining_size = 8;
677 - (void)sendLogin:(NSString *) password {
679 [m_login_handler usePass: password];
681 ECLoginRequestPacket *p = [ECLoginRequestPacket loginPacket:@"0.1"];
685 - (void)sendPacket:(ECPacket *) packet {
686 NSOutputStream *memstream = [NSOutputStream outputStreamToMemory];
689 [packet writeToSocket:memstream];
690 id data = [memstream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
692 m_tx_size = [data length];
693 NSLog(@"[EC] sending packet %d bytes\n", m_tx_size);
694 m_tx_ptr = [m_ostream write:(const uint8_t *)[data bytes] maxLength:[data length]];
695 NSLog(@"[EC] %d bytes sent\n", m_tx_ptr);
696 if ( m_tx_ptr == m_tx_size ) {
699 m_txbuf = (NSData *)data;
702 NSStreamStatus stream_status = [m_ostream streamStatus];
703 switch ( stream_status ) {
704 case NSStreamStatusNotOpen:
705 case NSStreamStatusClosed:
706 case NSStreamStatusError:
707 NSLog(@"[EC] error in output stream\n");
712 // NSLog(@"[EC] status in output stream=%d\n", stream_status);
716 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
718 case NSStreamEventOpenCompleted:
720 NSLog(@"[EC] open complete\n");
723 case NSStreamEventErrorOccurred:
725 NSError *e = [stream streamError];
726 NSString *description = [e localizedDescription];
727 NSLog(@"[EC] socket error [%s]\n", [description UTF8String]);
730 case NSStreamEventHasBytesAvailable:
732 uint8_t *data_ptr = (uint8_t *)[m_rxbuf mutableBytes];
733 unsigned int len = [m_rxbuf length];
735 len = [(NSInputStream *)stream read:data_ptr + m_rx_size maxLength:len];
737 NSLog(@"[EC] receiving %d bytes, %d in total, %d remaining\n", len, m_rx_size, m_remaining_size);
741 // Remote side must be closed connection
744 if ( [delegate respondsToSelector:@selector(handleError)] ) {
745 [delegate performSelector:@selector(handleError)];
749 int packet_offset = 0;
750 while ( total_len != 0 ) {
751 len = ( m_remaining_size > total_len ) ? total_len : m_remaining_size;
754 // are we still waiting for flags and size?
755 if ( m_rx_size < 8 ) {
756 if ( (m_rx_size + len) >= 8 ) {
757 // got flags and packet size - may proceed
758 //uint32_t flags = *(((uint32_t *)[m_rxbuf mutableBytes]) + 0);
759 uint32_t val32 = *((uint32_t *)(data_ptr + 4 + packet_offset));
761 int delta = 8 - m_rx_size;
763 m_remaining_size = ntohl(val32) - (len - delta);
765 NSLog(@"[EC] rx got flags+size, remaining count %d\n", m_remaining_size);
768 m_remaining_size -= len;
771 m_remaining_size -= len;
774 if ( m_remaining_size == 0 ) {
776 // full packet received, call handler
778 uint8_t *packet_start = data_ptr + packet_offset;
779 int packet_length = [m_rxbuf length] - packet_offset;
780 ECPacket *packet = [ECPacket packetFromBuffer:packet_start withLength:packet_length];
781 packet_offset += m_rx_size;
783 if ( [m_login_handler loginOK] ) {
785 NSLog(@"[EC] calling delegate\n");
787 if ( [delegate respondsToSelector:@selector(handlePacket:)] ) {
788 [delegate performSelector:@selector(handlePacket:) withObject:packet];
791 NSLog(@"[EC] login handler\n");
792 [m_login_handler handlePacket: packet];
794 m_remaining_size = 8;
801 case NSStreamEventHasSpaceAvailable:
803 if ( m_txbuf != nil ) {
804 int remaining = [m_txbuf length] - m_tx_ptr;
806 const uint8_t *txdata = ((const uint8_t *)[m_txbuf bytes]) + m_tx_ptr;
807 int txcount = [m_ostream write:txdata maxLength:remaining];
820 - (void)setDelegate:(id)val {
831 @implementation amuleLoginHandler
833 + (id)initWithConnection:(ECRemoteConnection *) connection {
835 amuleLoginHandler *obj = [[amuleLoginHandler alloc] init];
836 obj->m_connection = connection;
838 obj->m_state = LOGIN_REQUEST_SENT;
843 - (void)usePass:(NSString *) pass {
847 - (void)handlePacket:(ECPacket *) packet {
850 NSLog(@"[EC]: error - no packet should come until request is sent\n");
852 case LOGIN_REQUEST_SENT:
853 if ( packet.opcode == EC_OP_AUTH_SALT ) {
855 uint64_t salt = [packet tagInt64ByName:EC_TAG_PASSWD_SALT];
856 ECLoginAuthPacket *auth_packet = [ECLoginAuthPacket loginPacket:m_pass withSalt:salt];
857 [m_connection sendPacket:auth_packet];
859 m_state = LOGIN_PASS_SENT;
861 NSLog(@"[EC]: error - expecting packet with EC_OP_AUTH_SALT, not [%d]\n", packet.opcode);
862 m_state = LOGIN_IDLE;
865 case LOGIN_PASS_SENT:
866 if ( packet.opcode == EC_OP_AUTH_OK ) {
869 NSLog(@"[EC]: error - login failed, core replied with code=[%d]\n", packet.opcode);
873 NSLog(@"[EC]: error - this delegate should be replaced after login completed\n");
878 m_state = LOGIN_IDLE;
882 return m_state == LOGIN_OK;