Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import <MtProtoKit/MTEncryption.h>
void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
void MyAesCbcDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv);
+327
View File
@@ -0,0 +1,327 @@
#import "MTAes.h"
#import <CommonCrypto/CommonCrypto.h>
# define AES_MAXNR 14
# define AES_BLOCK_SIZE 16
#define N_WORDS (AES_BLOCK_SIZE / sizeof(unsigned long))
typedef struct {
unsigned long data[N_WORDS];
} aes_block_t;
/* XXX: probably some better way to do this */
#if defined(__i386__) || defined(__x86_64__)
# define UNALIGNED_MEMOPS_ARE_FAST 1
#else
# define UNALIGNED_MEMOPS_ARE_FAST 0
#endif
#if UNALIGNED_MEMOPS_ARE_FAST
# define load_block(d, s) (d) = *(const aes_block_t *)(s)
# define store_block(d, s) *(aes_block_t *)(d) = (s)
#else
# define load_block(d, s) memcpy((d).data, (s), AES_BLOCK_SIZE)
# define store_block(d, s) memcpy((d), (s).data, AES_BLOCK_SIZE)
#endif
void MyAesIgeEncrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
int len;
size_t n;
void const *inB;
void *outB;
unsigned char aesIv[AES_BLOCK_SIZE];
memcpy(aesIv, iv, AES_BLOCK_SIZE);
unsigned char ccIv[AES_BLOCK_SIZE];
memcpy(ccIv, iv + AES_BLOCK_SIZE, AES_BLOCK_SIZE);
assert(((size_t)inBytes | (size_t)outBytes | (size_t)aesIv | (size_t)ccIv) % sizeof(long) ==
0);
void *tmpInBytes = malloc(length);
len = length / AES_BLOCK_SIZE;
inB = inBytes;
outB = tmpInBytes;
aes_block_t *inp = (aes_block_t *)inB;
aes_block_t *outp = (aes_block_t *)outB;
for (n = 0; n < N_WORDS; ++n) {
outp->data[n] = inp->data[n];
}
--len;
inB += AES_BLOCK_SIZE;
outB += AES_BLOCK_SIZE;
void const *inBCC = inBytes;
aes_block_t const *iv3p = (aes_block_t *)ccIv;
if (len > 0) {
while (len) {
aes_block_t *inp = (aes_block_t *)inB;
aes_block_t *outp = (aes_block_t *)outB;
for (n = 0; n < N_WORDS; ++n) {
outp->data[n] = inp->data[n] ^ iv3p->data[n];
}
iv3p = inBCC;
--len;
inBCC += AES_BLOCK_SIZE;
inB += AES_BLOCK_SIZE;
outB += AES_BLOCK_SIZE;
}
}
size_t realOutLength = 0;
CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, 0, key, keyLength, aesIv, tmpInBytes, length, outBytes, length, &realOutLength);
free(tmpInBytes);
assert(result == kCCSuccess);
len = length / AES_BLOCK_SIZE;
aes_block_t const *ivp = inB;
aes_block_t *iv2p = (aes_block_t *)ccIv;
inB = inBytes;
outB = outBytes;
while (len) {
aes_block_t *inp = (aes_block_t *)inB;
aes_block_t *outp = (aes_block_t *)outB;
for (n = 0; n < N_WORDS; ++n) {
outp->data[n] ^= iv2p->data[n];
}
ivp = outp;
iv2p = inp;
--len;
inB += AES_BLOCK_SIZE;
outB += AES_BLOCK_SIZE;
}
memcpy(iv, ivp->data, AES_BLOCK_SIZE);
memcpy(iv + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
}
void MyAesIgeDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
assert(length % 16 == 0);
assert(length >= 0);
unsigned char aesIv[AES_BLOCK_SIZE];
memcpy(aesIv, iv, AES_BLOCK_SIZE);
unsigned char ccIv[AES_BLOCK_SIZE];
memcpy(ccIv, iv + AES_BLOCK_SIZE, AES_BLOCK_SIZE);
assert(((size_t)inBytes | (size_t)outBytes | (size_t)aesIv | (size_t)ccIv) % sizeof(long) ==
0);
CCCryptorRef decryptor = NULL;
CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &decryptor);
if (decryptor != NULL) {
int len;
size_t n;
len = length / AES_BLOCK_SIZE;
aes_block_t *ivp = (aes_block_t *)(aesIv);
aes_block_t *iv2p = (aes_block_t *)(ccIv);
while (len) {
aes_block_t tmp;
aes_block_t *inp = (aes_block_t *)inBytes;
aes_block_t *outp = (aes_block_t *)outBytes;
for (n = 0; n < N_WORDS; ++n)
tmp.data[n] = inp->data[n] ^ iv2p->data[n];
size_t dataOutMoved = 0;
CCCryptorStatus result = CCCryptorUpdate(decryptor, &tmp, AES_BLOCK_SIZE, outBytes, AES_BLOCK_SIZE, &dataOutMoved);
assert(result == kCCSuccess);
assert(dataOutMoved == AES_BLOCK_SIZE);
for (n = 0; n < N_WORDS; ++n)
outp->data[n] ^= ivp->data[n];
ivp = inp;
iv2p = outp;
inBytes += AES_BLOCK_SIZE;
outBytes += AES_BLOCK_SIZE;
--len;
}
memcpy(iv, ivp->data, AES_BLOCK_SIZE);
memcpy(iv + AES_BLOCK_SIZE, iv2p->data, AES_BLOCK_SIZE);
CCCryptorRelease(decryptor);
}
}
void MyAesCbcDecrypt(const void *inBytes, int length, void *outBytes, const void *key, int keyLength, void *iv) {
size_t outLength = 0;
CCCryptorStatus status = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, key, keyLength, iv, inBytes, length, outBytes, length, &outLength);
assert(status == kCCSuccess);
assert(outLength == length);
}
static void ctr128_inc(unsigned char *counter)
{
uint32_t n = 16, c = 1;
do {
--n;
c += counter[n];
counter[n] = (uint8_t)c;
c >>= 8;
} while (n);
}
static void ctr128_inc_aligned(unsigned char *counter)
{
size_t *data, c, d, n;
const union {
long one;
char little;
} is_endian = {
1
};
if (is_endian.little || ((size_t)counter % sizeof(size_t)) != 0) {
ctr128_inc(counter);
return;
}
data = (size_t *)counter;
c = 1;
n = 16 / sizeof(size_t);
do {
--n;
d = data[n] += c;
/* did addition carry? */
c = ((d - c) ^ d) >> (sizeof(size_t) * 8 - 1);
} while (n);
}
@interface MTAesCtr () {
CCCryptorRef _cryptor;
unsigned char _ivec[16] __attribute__((aligned(16)));
unsigned int _num;
unsigned char _ecount[16] __attribute__((aligned(16)));
}
@end
@implementation MTAesCtr
- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv decrypt:(bool)decrypt {
self = [super init];
if (self != nil) {
_num = 0;
memset(_ecount, 0, 16);
memcpy(_ivec, iv, 16);
CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &_cryptor);
}
return self;
}
- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv ecount:(void *)ecount num:(uint32_t)num {
self = [super init];
if (self != nil) {
_num = num;
memcpy(_ecount, ecount, 16);
memcpy(_ivec, iv, 16);
CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode, key, keyLength, nil, &_cryptor);
}
return self;
}
- (void)dealloc {
if (_cryptor) {
CCCryptorRelease(_cryptor);
}
}
- (uint32_t)num {
return _num;
}
- (void *)ecount {
return _ecount;
}
- (void)getIv:(void *)iv {
memcpy(iv, _ivec, 16);
}
- (void)encryptIn:(const unsigned char *)in out:(unsigned char *)out len:(size_t)len {
unsigned int n;
size_t l = 0;
assert(in);
assert(out);
assert(_num < 16);
n = _num;
if (16 % sizeof(size_t) == 0) { /* always true actually */
do {
while (n && len) {
*(out++) = *(in++) ^ _ecount[n];
--len;
n = (n + 1) % 16;
}
if (((size_t)in|(size_t)out|(size_t)_ivec)%sizeof(size_t) != 0)
break;
while (len >= 16) {
size_t dataOutMoved;
CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
ctr128_inc_aligned(_ivec);
for (n = 0; n < 16; n += sizeof(size_t)) {
*(size_t *)(out + n) =
*(size_t *)(in + n) ^ *(size_t *)(_ecount + n);
}
len -= 16;
out += 16;
in += 16;
n = 0;
}
if (len) {
size_t dataOutMoved;
CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
ctr128_inc_aligned(_ivec);
while (len--) {
out[n] = in[n] ^ _ecount[n];
++n;
}
}
_num = n;
return;
} while (0);
}
/* the rest would be commonly eliminated by x86* compiler */
while (l < len) {
if (n == 0) {
size_t dataOutMoved;
CCCryptorUpdate(_cryptor, _ivec, 16, _ecount, 16, &dataOutMoved);
ctr128_inc(_ivec);
}
out[l] = in[l] ^ _ecount[n];
++l;
n = (n + 1) % 16;
}
_num = n;
}
@end
@@ -0,0 +1,897 @@
#import <MtProtoKit/MTApiEnvironment.h>
#if TARGET_OS_IPHONE
# import <UIKit/UIKit.h>
#else
#endif
#include <sys/sysctl.h>
#import <CommonCrypto/CommonDigest.h>
static NSData * _Nullable parseHexString(NSString * _Nonnull hex) {
if ([hex length] % 2 != 0) {
return nil;
}
char buf[3];
buf[2] = '\0';
uint8_t *bytes = (uint8_t *)malloc(hex.length / 2);
uint8_t *bp = bytes;
for (CFIndex i = 0; i < [hex length]; i += 2) {
buf[0] = [hex characterAtIndex:i];
buf[1] = [hex characterAtIndex:i+1];
char *b2 = NULL;
*bp++ = strtol(buf, &b2, 16);
if (b2 != buf + 2) {
return nil;
}
}
return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES];
}
static NSString * _Nonnull dataToHexString(NSData * _Nonnull data) {
const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
if (dataBuffer == NULL) {
return @"";
}
NSUInteger dataLength = [data length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < (int)dataLength; ++i) {
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
}
return hexString;
}
static NSData *base64_decode(NSString *str) {
if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]];
#pragma clang diagnostic pop
}
}
@implementation MTProxySecret
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super init];
if (self != nil) {
_secret = secret;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self != nil) {
_secret = [aDecoder decodeObjectForKey:@"secret"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_secret forKey:@"secret"];
}
+ (MTProxySecret * _Nullable)parse:(NSString * _Nonnull)string {
NSData *hexData = parseHexString(string);
if (hexData == nil) {
NSString *finalString = @"";
finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
finalString = [finalString stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
finalString = [finalString stringByReplacingOccurrencesOfString:@"_" withString:@"/"];
while (finalString.length % 4 != 0) {
finalString = [finalString stringByAppendingString:@"="];
}
hexData = base64_decode(finalString);
}
if (hexData != nil) {
return [self parseData:hexData];
} else {
return nil;
}
}
+ (MTProxySecret * _Nullable)parseData:(NSData * _Nonnull)data {
if (data == nil || data.length < 16) {
return nil;
}
uint8_t firstByte = 0;
[data getBytes:&firstByte length:1];
if (data.length == 16) {
return [[MTProxySecretType0 alloc] initWithSecret:data];
} else if (data.length == 17) {
if (firstByte == 0xdd) {
return [[MTProxySecretType1 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)]];
} else {
return nil;
}
} else if (data.length >= 18 && firstByte == 0xee) {
NSString *domain = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(1 + 16, data.length - (1 + 16))] encoding:NSUTF8StringEncoding];
if (domain == nil) {
return nil;
}
return [[MTProxySecretType2 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)] domain:domain];
} else {
return nil;
}
}
- (NSData * _Nonnull)serialize {
assert(false);
return nil;
}
- (NSString * _Nonnull)serializeToString {
assert(false);
return nil;
}
- (NSString *)description {
return dataToHexString([self serialize]);
}
@end
@implementation MTProxySecretType0
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super initWithSecret:secret];
if (self != nil) {
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
}
- (NSData * _Nonnull)serialize {
return self.secret;
}
- (NSString * _Nonnull)serializeToString {
return dataToHexString(self.serialize);
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType0 class]]) {
return false;
}
MTProxySecretType0 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
return true;
}
@end
@implementation MTProxySecretType1
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super initWithSecret:secret];
if (self != nil) {
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
}
- (NSData * _Nonnull)serialize {
NSMutableData *data = [[NSMutableData alloc] init];
uint8_t marker = 0xdd;
[data appendBytes:&marker length:1];
[data appendData:self.secret];
return data;
}
- (NSString * _Nonnull)serializeToString {
return dataToHexString(self.serialize);
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType1 class]]) {
return false;
}
MTProxySecretType1 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
return true;
}
@end
@implementation MTProxySecretType2
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret domain:(NSString * _Nonnull)domain {
self = [super initWithSecret:secret];
if (self != nil) {
_domain = domain;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
_domain = [aDecoder decodeObjectForKey:@"domain"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_domain forKey:@"domain"];
}
- (NSData * _Nonnull)serialize {
NSMutableData *data = [[NSMutableData alloc] init];
uint8_t marker = 0xee;
[data appendBytes:&marker length:1];
[data appendData:self.secret];
[data appendData:[_domain dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
- (NSString * _Nonnull)serializeToString {
NSData *data = [self serialize];
if ([data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [[data base64EncodedStringWithOptions:kNilOptions] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [self.serialize base64Encoding];
#pragma clang diagnostic pop
}
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType2 class]]) {
return false;
}
MTProxySecretType2 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
if (![self.domain isEqual:other.domain]) {
return false;
}
return true;
}
@end
@implementation MTSocksProxySettings
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret {
self = [super init];
if (self != nil) {
_ip = ip;
_port = port;
_username = username;
_password = password;
_secret = secret;
}
return self;
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTSocksProxySettings class]]) {
return false;
}
MTSocksProxySettings *other = object;
if ((other->_ip != nil) != (_ip != nil) || (_ip != nil && ![_ip isEqual:other->_ip])) {
return false;
}
if (other->_port != _port) {
return false;
}
if ((other->_username != nil) != (_username != nil) || (_username != nil && ![_username isEqual:other->_username])) {
return false;
}
if ((other->_password != nil) != (_password != nil) || (_password != nil && ![_password isEqual:other->_password])) {
return false;
}
if ((other->_secret != nil) != (_secret != nil) || (_secret != nil && ![_secret isEqual:other->_secret])) {
return false;
}
return true;
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, _password, [_secret description]];
}
@end
@implementation MTNetworkSettings
- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout {
self = [super init];
if (self != nil) {
_reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout;
}
return self;
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTNetworkSettings class]]) {
return false;
}
MTNetworkSettings *other = object;
if (_reducedBackupDiscoveryTimeout != other->_reducedBackupDiscoveryTimeout) {
return false;
}
return true;
}
@end
@implementation MTApiEnvironment
-(instancetype)init {
self = [self initWithDeviceModelName:nil];
if (self != nil)
{
}
return self;
}
-(id _Nonnull)initWithDeviceModelName:(NSString * _Nullable)deviceModelName {
self = [super init];
if (self != nil)
{
if (deviceModelName != nil) {
_deviceModel = deviceModelName;
} else {
_deviceModel = [self platformString];
}
_deviceModelName = deviceModelName;
#if TARGET_OS_IPHONE
_systemVersion = [[UIDevice currentDevice] systemVersion];
#else
NSProcessInfo *pInfo = [NSProcessInfo processInfo];
_systemVersion = [[[pInfo operatingSystemVersionString] componentsSeparatedByString:@" "] objectAtIndex:1];
#endif
NSString *suffix = @"";
#if TARGET_OS_OSX
NSString *value = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"SOURCE"];
if (value != nil) {
suffix = [NSString stringWithFormat:@"%@", value];
}
#endif
//SOURCE
NSString *versionString = [[NSString alloc] initWithFormat:@"%@ (%@) %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"], suffix];
_appVersion = versionString;
_systemLangCode = [[NSLocale preferredLanguages] objectAtIndex:0];
#if TARGET_OS_OSX
_langPack = @"macos";
#else
_langPack = @"ios";
#endif
_langPackCode = @"";
[self _updateApiInitializationHash];
}
return self;
}
- (void)_updateApiInitializationHash {
_apiInitializationHash = [[NSString alloc] initWithFormat:@"apiId=%" PRId32 "&deviceModel=%@&systemVersion=%@&appVersion=%@&langCode=%@&layer=%@&langPack=%@&langPackCode=%@&proxy=%@&systemCode=%@", _apiId, _deviceModel, _systemVersion, _appVersion, _systemLangCode, _layer, _langPack, _langPackCode, _socksProxySettings, _systemCode];
}
- (void)setLayer:(NSNumber *)layer {
_layer = layer;
[self _updateApiInitializationHash];
}
- (void)setAppVersion:(NSString *)appVersion {
_appVersion = appVersion;
[self _updateApiInitializationHash];
}
- (void)setLangPack:(NSString *)langPack {
_langPack = langPack;
[self _updateApiInitializationHash];
}
- (void)setLangPackCode:(NSString *)langPackCode {
_langPackCode = langPackCode;
[self _updateApiInitializationHash];
}
- (NSString *)platformString
{
#if TARGET_OS_IPHONE
NSString *platform = [self platform];
if ([platform isEqualToString:@"iPhone1,1"])
return @"iPhone";
if ([platform isEqualToString:@"iPhone1,2"])
return @"iPhone 3G";
if ([platform isEqualToString:@"iPhone2,1"])
return @"iPhone 3GS";
if ([platform hasPrefix:@"iPhone3"])
return @"iPhone 4";
if ([platform hasPrefix:@"iPhone4"])
return @"iPhone 4S";
if ([platform isEqualToString:@"iPhone5,1"] ||
[platform isEqualToString:@"iPhone5,2"])
return @"iPhone 5";
if ([platform isEqualToString:@"iPhone5,3"] ||
[platform isEqualToString:@"iPhone5,4"])
return @"iPhone 5C";
if ([platform hasPrefix:@"iPhone6"])
return @"iPhone 5S";
if ([platform isEqualToString:@"iPhone7,1"])
return @"iPhone 6 Plus";
if ([platform isEqualToString:@"iPhone7,2"])
return @"iPhone 6";
if ([platform isEqualToString:@"iPhone8,1"])
return @"iPhone 6S";
if ([platform isEqualToString:@"iPhone8,2"])
return @"iPhone 6S Plus";
if ([platform isEqualToString:@"iPhone8,4"])
return @"iPhone SE";
if ([platform isEqualToString:@"iPhone9,1"] ||
[platform isEqualToString:@"iPhone9,3"])
return @"iPhone 7";
if ([platform isEqualToString:@"iPhone9,2"] ||
[platform isEqualToString:@"iPhone9,4"])
return @"iPhone 7 Plus";
if ([platform isEqualToString:@"iPhone10,1"] ||
[platform isEqualToString:@"iPhone10,4"])
return @"iPhone 8";
if ([platform isEqualToString:@"iPhone10,2"] ||
[platform isEqualToString:@"iPhone10,5"])
return @"iPhone 8 Plus";
if ([platform isEqualToString:@"iPhone10,3"] ||
[platform isEqualToString:@"iPhone10,6"])
return @"iPhone X";
if ([platform isEqualToString:@"iPhone11,2"])
return @"iPhone XS";
if ([platform isEqualToString:@"iPhone11,4"] ||
[platform isEqualToString:@"iPhone11,6"])
return @"iPhone XS Max";
if ([platform isEqualToString:@"iPhone11,8"])
return @"iPhone XR";
if ([platform isEqualToString:@"iPhone12,1"])
return @"iPhone 11";
if ([platform isEqualToString:@"iPhone12,3"])
return @"iPhone 11 Pro";
if ([platform isEqualToString:@"iPhone12,5"])
return @"iPhone 11 Pro Max";
if ([platform isEqualToString:@"iPhone12,8"])
return @"iPhone SE (2nd gen)";
if ([platform isEqualToString:@"iPhone13,1"])
return @"iPhone 12 mini";
if ([platform isEqualToString:@"iPhone13,2"])
return @"iPhone 12";
if ([platform isEqualToString:@"iPhone13,3"])
return @"iPhone 12 Pro";
if ([platform isEqualToString:@"iPhone13,4"])
return @"iPhone 12 Pro Max";
if ([platform isEqualToString:@"iPhone14,2"])
return @"iPhone 13 Pro";
if ([platform isEqualToString:@"iPhone14,3"])
return @"iPhone 13 Pro Max";
if ([platform isEqualToString:@"iPhone14,4"])
return @"iPhone 13 Mini";
if ([platform isEqualToString:@"iPhone14,5"])
return @"iPhone 13";
if ([platform isEqualToString:@"iPhone14,6"])
return @"iPhone SE (3rd gen)";
if ([platform isEqualToString:@"iPhone14,7"])
return @"iPhone 14";
if ([platform isEqualToString:@"iPhone14,8"])
return @"iPhone 14 Plus";
if ([platform isEqualToString:@"iPhone15,2"])
return @"iPhone 14 Pro";
if ([platform isEqualToString:@"iPhone15,3"])
return @"iPhone 14 Pro Max";
if ([platform isEqualToString:@"iPhone15,4"])
return @"iPhone 15";
if ([platform isEqualToString:@"iPhone15,5"])
return @"iPhone 15 Plus";
if ([platform isEqualToString:@"iPhone16,1"])
return @"iPhone 15 Pro";
if ([platform isEqualToString:@"iPhone16,2"])
return @"iPhone 15 Pro Max";
if ([platform isEqualToString:@"iPhone17,3"])
return @"iPhone 16";
if ([platform isEqualToString:@"iPhone17,4"])
return @"iPhone 16 Plus";
if ([platform isEqualToString:@"iPhone17,1"])
return @"iPhone 16 Pro";
if ([platform isEqualToString:@"iPhone17,2"])
return @"iPhone 16 Pro Max";
if ([platform isEqualToString:@"iPhone17,5"])
return @"iPhone 16e";
if ([platform isEqualToString:@"iPhone18,3"])
return @"iPhone 17";
if ([platform isEqualToString:@"iPhone18,1"])
return @"iPhone 17 Pro";
if ([platform isEqualToString:@"iPhone18,2"])
return @"iPhone 17 Pro Max";
if ([platform isEqualToString:@"iPhone18,4"])
return @"iPhone Air";
if ([platform hasPrefix:@"iPod1"])
return @"iPod touch 1G";
if ([platform hasPrefix:@"iPod2"])
return @"iPod touch 2G";
if ([platform hasPrefix:@"iPod3"])
return @"iPod touch 3G";
if ([platform hasPrefix:@"iPod4"])
return @"iPod touch 4G";
if ([platform hasPrefix:@"iPod5"])
return @"iPod touch 5G";
if ([platform hasPrefix:@"iPod7"])
return @"iPod touch 6G";
if ([platform hasPrefix:@"iPod9"])
return @"iPod touch 7G";
if ([platform isEqualToString:@"iPad2,5"] ||
[platform isEqualToString:@"iPad2,6"] ||
[platform isEqualToString:@"iPad2,7"])
return @"iPad mini";
if ([platform hasPrefix:@"iPad2"])
return @"iPad 2G";
if ([platform isEqualToString:@"iPad3,1"] ||
[platform isEqualToString:@"iPad3,2"] ||
[platform isEqualToString:@"iPad3,3"])
return @"iPad 3G";
if ([platform isEqualToString:@"iPad3,4"] ||
[platform isEqualToString:@"iPad3,5"] ||
[platform isEqualToString:@"iPad3,6"])
return @"iPad 3G";
if ([platform isEqualToString:@"iPad4,1"] ||
[platform isEqualToString:@"iPad4,2"])
return @"iPad Air";
if ([platform isEqualToString:@"iPad4,4"] ||
[platform isEqualToString:@"iPad4,5"] ||
[platform isEqualToString:@"iPad4,6"])
return @"iPad mini Retina";
if ([platform isEqualToString:@"iPad4,7"] ||
[platform isEqualToString:@"iPad4,8"] ||
[platform isEqualToString:@"iPad4,9"])
return @"iPad mini 3";
if ([platform isEqualToString:@"iPad5,1"] ||
[platform isEqualToString:@"iPad5,2"])
return @"iPad mini 4";
if ([platform isEqualToString:@"iPad5,3"] ||
[platform isEqualToString:@"iPad5,4"])
return @"iPad Air 2";
if ([platform isEqualToString:@"iPad6,3"] ||
[platform isEqualToString:@"iPad6,4"])
return @"iPad Pro 9.7 inch";
if ([platform isEqualToString:@"iPad6,7"] ||
[platform isEqualToString:@"iPad6,8"])
return @"iPad Pro 12.9 inch";
if ([platform isEqualToString:@"iPad6,11"] ||
[platform isEqualToString:@"iPad6,12"])
return @"iPad (2017)";
if ([platform isEqualToString:@"iPad7,1"] ||
[platform isEqualToString:@"iPad7,2"])
return @"iPad Pro (2nd gen)";
if ([platform isEqualToString:@"iPad7,3"] ||
[platform isEqualToString:@"iPad7,4"])
return @"iPad Pro 10.5 inch";
if ([platform isEqualToString:@"iPad7,5"] ||
[platform isEqualToString:@"iPad7,6"])
return @"iPad (6th gen)";
if ([platform isEqualToString:@"iPad7,11"] ||
[platform isEqualToString:@"iPad7,12"])
return @"iPad 10.2 inch (7th gen)";
if ([platform isEqualToString:@"iPad8,1"] ||
[platform isEqualToString:@"iPad8,2"] ||
[platform isEqualToString:@"iPad8,3"] ||
[platform isEqualToString:@"iPad8,4"])
return @"iPad Pro 11 inch";
if ([platform isEqualToString:@"iPad8,5"] ||
[platform isEqualToString:@"iPad8,6"] ||
[platform isEqualToString:@"iPad8,7"] ||
[platform isEqualToString:@"iPad8,8"])
return @"iPad Pro 12.9 inch (3rd gen)";
if ([platform isEqualToString:@"iPad8,9"] ||
[platform isEqualToString:@"iPad8,10"])
return @"iPad Pro 11 inch (2th gen)";
if ([platform isEqualToString:@"iPad8,11"] ||
[platform isEqualToString:@"iPad8,12"])
return @"iPad Pro 12.9 inch (4th gen)";
if ([platform isEqualToString:@"iPad11,1"] ||
[platform isEqualToString:@"iPad11,2"])
return @"iPad mini (5th gen)";
if ([platform isEqualToString:@"iPad11,3"] ||
[platform isEqualToString:@"iPad11,4"])
return @"iPad Air (3rd gen)";
if ([platform isEqualToString:@"iPad11,6"] ||
[platform isEqualToString:@"iPad11,7"])
return @"iPad (8th gen)";
if ([platform isEqualToString:@"iPad12,1"] ||
[platform isEqualToString:@"iPad12,2"])
return @"iPad (9th gen)";
if ([platform isEqualToString:@"iPad13,1"] ||
[platform isEqualToString:@"iPad13,2"])
return @"iPad Air (4th gen)";
if ([platform isEqualToString:@"iPad13,4"] ||
[platform isEqualToString:@"iPad13,5"] ||
[platform isEqualToString:@"iPad13,6"] ||
[platform isEqualToString:@"iPad13,7"])
return @"iPad Pro 11 inch (3th gen)";
if ([platform isEqualToString:@"iPad13,8"] ||
[platform isEqualToString:@"iPad13,9"] ||
[platform isEqualToString:@"iPad13,10"] ||
[platform isEqualToString:@"iPad13,11"])
return @"iPad Pro 12.9 inch (5th gen)";
if ([platform isEqualToString:@"iPad13,16"] ||
[platform isEqualToString:@"iPad13,17"])
return @"iPad Air (5th gen)";
if ([platform isEqualToString:@"iPad13,18"] ||
[platform isEqualToString:@"iPad13,19"])
return @"iPad (10th gen)";
if ([platform isEqualToString:@"iPad14,1"] ||
[platform isEqualToString:@"iPad14,2"])
return @"iPad mini (6th gen)";
if ([platform isEqualToString:@"iPad14,3"] ||
[platform isEqualToString:@"iPad14,4"])
return @"iPad Pro 11 inch (4th gen)";
if ([platform isEqualToString:@"iPad14,5"] ||
[platform isEqualToString:@"iPad14,6"])
return @"iPad Pro 12.9 inch (6th gen)";
if ([platform isEqualToString:@"iPad14,8"] ||
[platform isEqualToString:@"iPad14,9"])
return @"iPad Air (6th gen)";
if ([platform isEqualToString:@"iPad14,10"] ||
[platform isEqualToString:@"iPad14,11"])
return @"iPad Air (7th gen)";
if ([platform isEqualToString:@"iPad16,3"] ||
[platform isEqualToString:@"iPad16,4"])
return @"iPad Pro 11 inch (5th gen)";
if ([platform isEqualToString:@"iPad16,5"] ||
[platform isEqualToString:@"iPad16,6"])
return @"iPad Pro 12.9 inch (7th gen)";
if ([platform hasPrefix:@"iPhone"])
return @"Unknown iPhone";
if ([platform hasPrefix:@"iPod"])
return @"Unknown iPod";
if ([platform hasPrefix:@"iPad"])
return @"Unknown iPad";
if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"] || [platform isEqual:@"arm64"]) {
return @"iPhone Simulator";
}
#else
return [self macHWName];
#endif
return @"Unknown iOS device";
}
- (NSString *)macHWName {
size_t len = 0;
sysctlbyname("hw.model", NULL, &len, NULL, 0);
if (len) {
char *model = malloc(len*sizeof(char));
sysctlbyname("hw.model", model, &len, NULL, 0);
NSString *name = [[NSString alloc] initWithUTF8String:model];
free(model);
return name;
};
return @"macOS";
}
- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
size_t size;
sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
char *answer = malloc(size);
sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
free(answer);
return results;
}
- (NSString *)platform
{
return [self getSysInfoByName:"hw.machine"];
}
- (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode {
MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName];
result.apiId = self.apiId;
result.appVersion = self.appVersion;
result.layer = self.layer;
result.langPack = self.langPack;
result->_langPackCode = langPackCode;
result.disableUpdates = self.disableUpdates;
result.tcpPayloadPrefix = self.tcpPayloadPrefix;
result.datacenterAddressOverrides = self.datacenterAddressOverrides;
result.accessHostOverride = self.accessHostOverride;
result->_socksProxySettings = self.socksProxySettings;
result->_networkSettings = self.networkSettings;
result->_systemCode = self.systemCode;
[result _updateApiInitializationHash];
return result;
}
- (instancetype)copyWithZone:(NSZone *)__unused zone {
MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName];
result.apiId = self.apiId;
result.appVersion = self.appVersion;
result.layer = self.layer;
result.langPack = self.langPack;
result->_langPackCode = self.langPackCode;
result->_socksProxySettings = self.socksProxySettings;
result->_networkSettings = self.networkSettings;
result->_systemCode = self.systemCode;
result.disableUpdates = self.disableUpdates;
result.tcpPayloadPrefix = self.tcpPayloadPrefix;
result.datacenterAddressOverrides = self.datacenterAddressOverrides;
result.accessHostOverride = self.accessHostOverride;
[result _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings {
MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName];
result.apiId = self.apiId;
result.appVersion = self.appVersion;
result.layer = self.layer;
result.langPack = self.langPack;
result->_langPackCode = self.langPackCode;
result->_socksProxySettings = socksProxySettings;
result->_networkSettings = self.networkSettings;
result->_systemCode = self.systemCode;
result.disableUpdates = self.disableUpdates;
result.tcpPayloadPrefix = self.tcpPayloadPrefix;
result.datacenterAddressOverrides = self.datacenterAddressOverrides;
result.accessHostOverride = self.accessHostOverride;
[result _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings {
MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName];
result.apiId = self.apiId;
result.appVersion = self.appVersion;
result.layer = self.layer;
result.langPack = self.langPack;
result->_langPackCode = self.langPackCode;
result->_socksProxySettings = self.socksProxySettings;
result->_networkSettings = networkSettings;
result->_systemCode = self.systemCode;
result.disableUpdates = self.disableUpdates;
result.tcpPayloadPrefix = self.tcpPayloadPrefix;
result.datacenterAddressOverrides = self.datacenterAddressOverrides;
result.accessHostOverride = self.accessHostOverride;
[result _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode {
MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName];
result.apiId = self.apiId;
result.appVersion = self.appVersion;
result.layer = self.layer;
result.langPack = self.langPack;
result->_langPackCode = self.langPackCode;
result->_socksProxySettings = self.socksProxySettings;
result->_networkSettings = self.networkSettings;
result->_systemCode = systemCode;
result.disableUpdates = self.disableUpdates;
result.tcpPayloadPrefix = self.tcpPayloadPrefix;
result.datacenterAddressOverrides = self.datacenterAddressOverrides;
result.accessHostOverride = self.accessHostOverride;
[result _updateApiInitializationHash];
return result;
}
@end
+64
View File
@@ -0,0 +1,64 @@
#import <MtProtoKit/MTAtomic.h>
#import <os/lock.h>
@interface MTAtomic ()
{
os_unfair_lock _lock;
id _value;
}
@end
@implementation MTAtomic
- (instancetype)initWithValue:(id)value
{
self = [super init];
if (self != nil)
{
_value = value;
}
return self;
}
- (id)swap:(id)newValue
{
id previousValue = nil;
os_unfair_lock_lock(&_lock);
previousValue = _value;
_value = newValue;
os_unfair_lock_unlock(&_lock);
return previousValue;
}
- (id)value
{
id previousValue = nil;
os_unfair_lock_lock(&_lock);
previousValue = _value;
os_unfair_lock_unlock(&_lock);
return previousValue;
}
- (id)modify:(id (^)(id))f
{
id newValue = nil;
os_unfair_lock_lock(&_lock);
newValue = f(_value);
_value = newValue;
os_unfair_lock_unlock(&_lock);
return newValue;
}
- (id)with:(id (^)(id))f
{
id result = nil;
os_unfair_lock_lock(&_lock);
result = f(_value);
os_unfair_lock_unlock(&_lock);
return result;
}
@end
@@ -0,0 +1,412 @@
#import <MtProtoKit/MTBackupAddressSignals.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTAtomic.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTHttpRequestOperation.h>
#import <MtProtoKit/MTEncryption.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTRequest.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTDatacenterAddress.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTKeychain.h>
@interface MTTemporaryKeychain : NSObject<MTKeychain> {
NSMutableDictionary<NSString *, id> *_dict;
}
@end
@implementation MTTemporaryKeychain
- (instancetype)init {
self = [super init];
if (self != nil) {
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSString *)itemKeyForGroup:(NSString *)group key:(NSString *)key {
return [NSString stringWithFormat:@"%@:%@", group, key];
}
- (void)setObject:(id)object forKey:(NSString *)aKey group:(NSString *)group {
if (object == nil) {
return;
}
_dict[[self itemKeyForGroup:group key:aKey]] = object;
}
- (NSDictionary *)dictionaryForKey:(NSString *)aKey group:(NSString *)group {
id result = _dict[[self itemKeyForGroup:group key:aKey]];
if ([result isKindOfClass:[NSDictionary class]]) {
return result;
} else {
return nil;
}
}
- (NSNumber *)numberForKey:(NSString *)aKey group:(NSString *)group {
id result = _dict[[self itemKeyForGroup:group key:aKey]];
if ([result isKindOfClass:[NSNumber class]]) {
return result;
} else {
return nil;
}
}
- (void)removeObjectForKey:(NSString *)aKey group:(NSString *)group {
[_dict removeObjectForKey:[self itemKeyForGroup:group key:aKey]];
}
@end
static NSData *base64_decode(NSString *str) {
if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]];
#pragma clang diagnostic pop
}
}
@implementation MTBackupAddressSignals
+ (bool)checkIpData:(MTBackupDatacenterData *)data timestamp:(int32_t)timestamp source:(NSString *)source {
if (data.timestamp >= timestamp + 60 * 20 || data.expirationDate <= timestamp - 60 * 20) {
if (MTLogEnabled()) {
MTLog(@"[Backup address fetch: backup config from %@ validity interval %d ... %d does not include current %d]", source, data.timestamp, data.expirationDate, timestamp);
}
return false;
} else {
return true;
}
}
+ (MTSignal *)fetchBackupIpsResolveGoogle:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext addressOverride:(NSString *)addressOverride {
NSArray *hosts = @[
@[@"dns.google.com", @""],
@[@"www.google.com", @"dns.google.com"],
];
id<EncryptionProvider> encryptionProvider = currentContext.encryptionProvider;
NSMutableArray *signals = [[NSMutableArray alloc] init];
for (NSArray *hostAndHostname in hosts) {
NSString *host = hostAndHostname[0];
NSString *hostName = hostAndHostname[1];
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
if ([hostName length] != 0) {
headers[@"Host"] = hostName;
}
NSString *apvHost = @"apv3.stel.com";
if (addressOverride != nil) {
apvHost = addressOverride;
}
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16&random_padding=%@", host, isTesting ? @"tapv3.stel.com" : apvHost, makeRandomPadding()]] headers:headers] mapToSignal:^MTSignal *(MTHttpResponse *response) {
NSString *dateHeader = response.headers[@"Date"];
if ([dateHeader isKindOfClass:[NSString class]]) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[formatter setLocale:usLocale];
[formatter setDateFormat:@"EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz"];
NSDate *date = [formatter dateFromString:dateHeader];
if (date != nil) {
double difference = [date timeIntervalSince1970] - [[NSDate date] timeIntervalSince1970];
[MTContext setFixedTimeDifference:(int32_t)difference];
}
}
NSData *data = response.data;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([dict respondsToSelector:@selector(objectForKey:)]) {
NSArray *answer = dict[@"Answer"];
NSMutableArray *strings = [[NSMutableArray alloc] init];
if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
for (NSDictionary *value in answer) {
if ([value respondsToSelector:@selector(objectForKey:)]) {
NSString *part = value[@"data"];
if ([part respondsToSelector:@selector(characterAtIndex:)]) {
[strings addObject:part];
}
}
}
[strings sortUsingComparator:^NSComparisonResult(NSString *lhs, NSString *rhs) {
if (lhs.length > rhs.length) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}];
NSString *finalString = @"";
for (NSString *string in strings) {
finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
}
NSData *result = base64_decode(finalString);
NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
[finalData setLength:256];
MTBackupDatacenterData *datacenterData = MTIPDataDecode(encryptionProvider, finalData, phoneNumber);
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveGoogle"]) {
return [MTSignal single:datacenterData];
}
}
}
return [MTSignal complete];
}] catch:^MTSignal *(__unused id error) {
return [MTSignal complete];
}];
if (signals.count != 0) {
signal = [signal delay:signals.count onQueue:[[MTQueue alloc] init]];
}
[signals addObject:signal];
}
return [[MTSignal mergeSignals:signals] take:1];
}
static NSString *makeRandomPadding() {
char validCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int maxIndex = sizeof(validCharacters) - 1;
int minPadding = 13;
int maxPadding = 128;
int padding = minPadding + arc4random_uniform(maxPadding - minPadding);
NSMutableData *result = [[NSMutableData alloc] initWithLength:padding];
for (NSUInteger i = 0; i < result.length; i++) {
int index = arc4random_uniform(maxIndex);
assert(index >= 0 && index < maxIndex);
((uint8_t *)(result.mutableBytes))[i] = validCharacters[index];
}
NSString *string = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
return string;
}
+ (MTSignal *)fetchBackupIpsResolveCloudflare:(bool)isTesting phoneNumber:(NSString *)phoneNumber currentContext:(MTContext *)currentContext addressOverride:(NSString *)addressOverride {
id<EncryptionProvider> encryptionProvider = currentContext.encryptionProvider;
NSArray *hosts = @[
@[@"mozilla.cloudflare-dns.com", @""],
];
NSMutableArray *signals = [[NSMutableArray alloc] init];
for (NSArray *hostAndHostname in hosts) {
NSString *host = hostAndHostname[0];
NSString *hostName = hostAndHostname[1];
NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
headers[@"accept"] = @"application/dns-json";
if ([hostName length] != 0) {
headers[@"Host"] = hostName;
}
NSString *apvHost = @"apv3.stel.com";
if (addressOverride != nil) {
apvHost = addressOverride;
}
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/dns-query?name=%@&type=16&random_padding=%@", host, isTesting ? @"tapv3.stel.com" : apvHost, makeRandomPadding()]] headers:headers] mapToSignal:^MTSignal *(MTHttpResponse *response) {
NSString *dateHeader = response.headers[@"Date"];
if ([dateHeader isKindOfClass:[NSString class]]) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[formatter setLocale:usLocale];
[formatter setDateFormat:@"EEE',' dd' 'MMM' 'yyyy HH':'mm':'ss zzz"];
NSDate *date = [formatter dateFromString:dateHeader];
if (date != nil) {
double difference = [date timeIntervalSince1970] - [[NSDate date] timeIntervalSince1970];
[MTContext setFixedTimeDifference:(int32_t)difference];
}
}
NSData *data = response.data;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([dict respondsToSelector:@selector(objectForKey:)]) {
NSArray *answer = dict[@"Answer"];
NSMutableArray *strings = [[NSMutableArray alloc] init];
if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
for (NSDictionary *value in answer) {
if ([value respondsToSelector:@selector(objectForKey:)]) {
NSString *part = value[@"data"];
if ([part respondsToSelector:@selector(characterAtIndex:)]) {
[strings addObject:part];
}
}
}
[strings sortUsingComparator:^NSComparisonResult(NSString *lhs, NSString *rhs) {
if (lhs.length > rhs.length) {
return NSOrderedAscending;
} else {
return NSOrderedDescending;
}
}];
NSString *finalString = @"";
for (NSString *string in strings) {
finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
}
NSData *result = base64_decode(finalString);
NSMutableData *finalData = [[NSMutableData alloc] initWithData:result];
[finalData setLength:256];
MTBackupDatacenterData *datacenterData = MTIPDataDecode(encryptionProvider, finalData, phoneNumber);
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveCloudflare"]) {
return [MTSignal single:datacenterData];
}
}
}
return [MTSignal complete];
}] catch:^MTSignal *(__unused id error) {
return [MTSignal complete];
}];
if (signals.count != 0) {
signal = [signal delay:signals.count onQueue:[[MTQueue alloc] init]];
}
[signals addObject:signal];
}
return [[MTSignal mergeSignals:signals] take:1];
}
MTAtomic *sharedFetchConfigKeychains() {
static MTAtomic *value = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
value = [[MTAtomic alloc] initWithValue:[[NSMutableDictionary alloc] init]];
});
return value;
}
+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext mainDatacenterId:(NSInteger)mainDatacenterId {
MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
apiEnvironment = [apiEnvironment withUpdatedSocksProxySettings:nil];
NSMutableDictionary *datacenterAddressOverrides = [[NSMutableDictionary alloc] init];
datacenterAddressOverrides[@(address.datacenterId)] = [[MTDatacenterAddress alloc] initWithIp:address.ip port:(uint16_t)address.port preferForMedia:false restrictToTcp:false cdn:false preferForProxy:false secret:address.secret];
apiEnvironment.datacenterAddressOverrides = datacenterAddressOverrides;
apiEnvironment.apiId = currentContext.apiEnvironment.apiId;
apiEnvironment.layer = currentContext.apiEnvironment.layer;
apiEnvironment = [apiEnvironment withUpdatedLangPackCode:currentContext.apiEnvironment.langPackCode];
apiEnvironment.disableUpdates = true;
apiEnvironment.langPack = currentContext.apiEnvironment.langPack;
MTContext *context = [[MTContext alloc] initWithSerialization:currentContext.serialization encryptionProvider:currentContext.encryptionProvider apiEnvironment:apiEnvironment isTestingEnvironment:currentContext.isTestingEnvironment useTempAuthKeys:false];
context.makeTcpConnectionInterface = currentContext.makeTcpConnectionInterface;
NSInteger authTokenMasterDatacenterId = 0;
NSNumber *requiredAuthToken = nil;
bool allowUnboundEphemeralKeys = true;
NSString *keychainKey = [NSString stringWithFormat:@"%d:%@:%d", (int)address.datacenterId, address.ip, (int)address.port];
MTTemporaryKeychain *tempKeychain = [sharedFetchConfigKeychains() with:^(NSMutableDictionary *dict) {
if (dict[keychainKey] != nil) {
return (MTTemporaryKeychain *)dict[keychainKey];
} else {
MTTemporaryKeychain *keychain = [[MTTemporaryKeychain alloc] init];
dict[keychainKey] = keychain;
return keychain;
}
}];
context.keychain = tempKeychain;
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:address.datacenterId usageCalculationInfo:nil requiredAuthToken:requiredAuthToken authTokenMasterDatacenterId:authTokenMasterDatacenterId];
mtProto.useTempAuthKeys = true;
mtProto.allowUnboundEphemeralKeys = allowUnboundEphemeralKeys;
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
[mtProto addMessageService:requestService];
[mtProto resume];
MTRequest *request = [[MTRequest alloc] init];
NSData *getConfigData = nil;
MTRequestDatacenterAddressListParser responseParser = [currentContext.serialization requestDatacenterAddressWithData:&getConfigData];
[request setPayload:getConfigData metadata:@"getConfig" shortMetadata:@"getConfig" responseParser:responseParser];
__weak MTContext *weakCurrentContext = currentContext;
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
[request setCompleted:^(MTDatacenterAddressListData *result, __unused MTRequestResponseInfo *info, id error)
{
if (error == nil) {
__strong MTContext *strongCurrentContext = weakCurrentContext;
if (strongCurrentContext != nil) {
[result.addressList enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, NSArray *list, __unused BOOL *stop) {
MTDatacenterAddressSet *addressSet = [[MTDatacenterAddressSet alloc] initWithAddressList:list];
MTDatacenterAddressSet *currentAddressSet = [context addressSetForDatacenterWithId:[nDatacenterId integerValue]];
if (currentAddressSet == nil || ![addressSet isEqual:currentAddressSet])
{
if (MTLogEnabled()) {
MTLog(@"[Backup address fetch: updating datacenter %d address set to %@]", [nDatacenterId intValue], addressSet);
}
[strongCurrentContext updateAddressSetForDatacenterWithId:[nDatacenterId integerValue] addressSet:addressSet forceUpdateSchemes:true];
[subscriber putNext:@true];
[subscriber putCompletion];
}
}];
}
} else {
[subscriber putCompletion];
}
}];
[requestService addRequest:request];
id requestId = request.internalId;
return [[MTBlockDisposable alloc] initWithBlock:^{
[requestService removeRequestByInternalId:requestId];
[mtProto pause];
}];
}];
}
+ (MTSignal * _Nonnull)fetchBackupIps:(bool)isTestingEnvironment currentContext:(MTContext * _Nonnull)currentContext additionalSource:(MTSignal * _Nullable)additionalSource phoneNumber:(NSString * _Nullable)phoneNumber mainDatacenterId:(NSInteger)mainDatacenterId {
NSMutableArray *signals = [[NSMutableArray alloc] init];
[signals addObject:[self fetchBackupIpsResolveGoogle:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext addressOverride:currentContext.apiEnvironment.accessHostOverride]];
[signals addObject:[self fetchBackupIpsResolveCloudflare:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext addressOverride:currentContext.apiEnvironment.accessHostOverride]];
if (additionalSource != nil) {
[signals addObject:[additionalSource mapToSignal:^MTSignal *(MTBackupDatacenterData *datacenterData) {
if (![datacenterData isKindOfClass:[MTBackupDatacenterData class]]) {
return [MTSignal complete];
}
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveExternal"]) {
return [MTSignal single:datacenterData];
} else {
return [MTSignal complete];
}
}]];
}
return [[[MTSignal mergeSignals:signals] take:1] mapToSignal:^MTSignal *(MTBackupDatacenterData *data) {
if (data != nil && data.addressList.count != 0) {
NSMutableArray *signals = [[NSMutableArray alloc] init];
NSTimeInterval delay = 0.0;
for (MTBackupDatacenterAddress *address in data.addressList) {
MTSignal *signal = [self fetchConfigFromAddress:address currentContext:currentContext mainDatacenterId:mainDatacenterId];
if (delay > DBL_EPSILON) {
signal = [signal delay:delay onQueue:[[MTQueue alloc] init]];
}
[signals addObject:signal];
delay += 5.0;
}
return [[MTSignal mergeSignals:signals] take:1];
}
return [MTSignal complete];
}];
}
@end
@@ -0,0 +1,19 @@
#import <Foundation/Foundation.h>
@interface MTBadMsgNotificationMessage : NSObject
@property (nonatomic, readonly) int64_t badMessageId;
@property (nonatomic, readonly) int32_t badMessageSeqNo;
@property (nonatomic, readonly) int32_t errorCode;
- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode;
@end
@interface MTBadServerSaltNotificationMessage : MTBadMsgNotificationMessage
@property (nonatomic, readonly) int64_t nextServerSalt;
- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode nextServerSalt:(int64_t)nextServerSalt;
@end
@@ -0,0 +1,31 @@
#import "MTBadMsgNotificationMessage.h"
@implementation MTBadMsgNotificationMessage
- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode
{
self = [super init];
if (self != nil)
{
_badMessageId = badMessageId;
_badMessageSeqNo = badMessageSeqNo;
_errorCode = errorCode;
}
return self;
}
@end
@implementation MTBadServerSaltNotificationMessage
- (instancetype)initWithBadMessageId:(int64_t)badMessageId badMessageSeqNo:(int32_t)badMessageSeqNo errorCode:(int32_t)errorCode nextServerSalt:(int64_t)nextServerSalt
{
self = [super initWithBadMessageId:badMessageId badMessageSeqNo:badMessageSeqNo errorCode:errorCode];
if (self != nil)
{
_nextServerSalt = nextServerSalt;
}
return self;
}
@end
+74
View File
@@ -0,0 +1,74 @@
#import <MtProtoKit/MTBag.h>
@interface MTBag ()
{
NSInteger _nextKey;
NSMutableArray *_items;
NSMutableArray *_itemKeys;
}
@end
@implementation MTBag
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_items = [[NSMutableArray alloc] init];
_itemKeys = [[NSMutableArray alloc] init];
}
return self;
}
- (NSInteger)addItem:(id)item
{
if (item == nil)
return -1;
NSInteger key = _nextKey;
[_items addObject:item];
[_itemKeys addObject:@(key)];
_nextKey++;
return key;
}
- (void)enumerateItems:(void (^)(id))block
{
if (block)
{
for (id item in _items)
{
block(item);
}
}
}
- (void)removeItem:(NSInteger)key
{
NSUInteger index = 0;
for (NSNumber *itemKey in _itemKeys)
{
if ([itemKey integerValue] == key)
{
[_items removeObjectAtIndex:index];
[_itemKeys removeObjectAtIndex:index];
break;
}
index++;
}
}
- (bool)isEmpty
{
return _items.count == 0;
}
- (NSArray *)copyItems
{
return [[NSArray alloc] initWithArray:_items];
}
@end
@@ -0,0 +1,175 @@
#import <MtProtoKit/MTBindKeyMessageService.h>
#import <MtProtoKit/MTTime.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTOutgoingMessage.h>
#import <MtProtoKit/MTIncomingMessage.h>
#import <MtProtoKit/MTPreparedMessage.h>
#import <MtProtoKit/MTMessageTransaction.h>
#import <MtProtoKit/MTDatacenterSaltInfo.h>
#import <MtProtoKit/MTSessionInfo.h>
#import <MtProtoKit/MTRpcError.h>
#import <MtProtoKit/MTLogging.h>
#import "MTInternalMessageParser.h"
#import "MTRpcResultMessage.h"
#import "MTBuffer.h"
@interface MTBindKeyMessageService () {
MTDatacenterAuthKey *_persistentKey;
MTDatacenterAuthKey *_ephemeralKey;
void (^_completion)(bool);
int64_t _currentMessageId;
id _currentTransactionId;
}
@end
@implementation MTBindKeyMessageService
- (instancetype)initWithPersistentKey:(MTDatacenterAuthKey *)persistentKey ephemeralKey:(MTDatacenterAuthKey *)ephemeralKey completion:(void (^)(bool))completion {
self = [super init];
if (self != nil) {
_persistentKey = persistentKey;
_ephemeralKey = ephemeralKey;
_completion = [completion copy];
}
return self;
}
- (void)mtProtoDidAddService:(MTProto *)mtProto
{
[mtProto requestTransportTransaction];
}
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo scheme:(MTTransportScheme *)scheme
{
if (_currentTransactionId != nil) {
return nil;
}
int64_t bindingMessageId = [sessionInfo generateClientMessageId:NULL];
int32_t bindingSeqNo = [sessionInfo takeSeqNo:true];
int32_t expiresAt = (int32_t)([mtProto.context globalTime] + mtProto.context.tempKeyExpiration);
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
int64_t nonce = 0;
arc4random_buf(&nonce, 8);
MTBuffer *decryptedMessage = [[MTBuffer alloc] init];
//bind_auth_key_inner#75a3f765 nonce:long temp_auth_key_id:long perm_auth_key_id:long temp_session_id:long expires_at:int = BindAuthKeyInner;
[decryptedMessage appendInt32:(int32_t)0x75a3f765];
[decryptedMessage appendInt64:nonce];
[decryptedMessage appendInt64:_ephemeralKey.authKeyId];
[decryptedMessage appendInt64:_persistentKey.authKeyId];
[decryptedMessage appendInt64:sessionInfo.sessionId];
[decryptedMessage appendInt32:expiresAt];
NSData *encryptedMessage = [MTProto _manuallyEncryptedMessage:[decryptedMessage data] messageId:bindingMessageId authKey:_persistentKey];
MTBuffer *bindRequestData = [[MTBuffer alloc] init];
//auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
[bindRequestData appendInt32:(int32_t)0xcdd42a05];
[bindRequestData appendInt64:_persistentKey.authKeyId];
[bindRequestData appendInt64:nonce];
[bindRequestData appendInt32:expiresAt];
[bindRequestData appendTLBytes:encryptedMessage];
if (MTLogEnabled()) {
MTLog(@"[MTBindKeyMessageService#%p binding temp %" PRId64 " to persistent %" PRId64 "]", self, _ephemeralKey.authKeyId, _persistentKey.authKeyId);
}
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:bindRequestData.data metadata:[NSString stringWithFormat:@"auth.bindTempAuthKey"] additionalDebugDescription:nil shortMetadata:@"auth.bindTempAuthKey" messageId:bindingMessageId messageSeqNo:bindingSeqNo];
return [[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId) {
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[outgoingMessage.internalId];
if (preparedMessage != nil && messageInternalIdToTransactionId[outgoingMessage.internalId] != nil) {
_currentMessageId = preparedMessage.messageId;
_currentTransactionId = messageInternalIdToTransactionId[outgoingMessage.internalId];
}
}];
return nil;
}
- (void)mtProto:(MTProto *)__unused mtProto messageDeliveryFailed:(int64_t)messageId {
if (messageId == _currentMessageId) {
_currentMessageId = 0;
_currentTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds {
if (_currentTransactionId != nil && [transactionIds containsObject:_currentTransactionId]) {
_currentTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto {
if (_currentTransactionId != nil) {
_currentTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProtoDidChangeSession:(MTProto *)mtProto {
_currentMessageId = 0;
_currentTransactionId = nil;
[mtProto requestTransportTransaction];
}
- (void)mtProtoServerDidChangeSession:(MTProto *)mtProto firstValidMessageId:(int64_t)firstValidMessageId messageIdsInFirstValidContainer:(NSArray *)messageIdsInFirstValidContainer {
if (_currentMessageId != 0 && _currentMessageId < firstValidMessageId && ![messageIdsInFirstValidContainer containsObject:@(_currentMessageId)]) {
_currentMessageId = 0;
_currentTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector networkType:(int32_t)networkType {
if ([message.body isKindOfClass:[MTRpcResultMessage class]]) {
MTRpcResultMessage *rpcResultMessage = message.body;
if (rpcResultMessage.requestMessageId == _currentMessageId) {
bool success = false;
if (rpcResultMessage.data.length >= 4) {
uint32_t signature = 0;
[rpcResultMessage.data getBytes:&signature range:NSMakeRange(0, 4)];
id parsedMessage = [MTInternalMessageParser parseMessage:rpcResultMessage.data];
if ([parsedMessage isKindOfClass:[MTRpcError class]]) {
MTRpcError *rpcError = (MTRpcError *)parsedMessage;
if (MTLogEnabled()) {
MTLog(@"[MTRequestMessageService#%p response for %" PRId64 " is error: %d: %@]", self, _currentMessageId, (int)rpcError.errorCode, rpcError.errorDescription);
}
MTShortLog(@"[MTRequestMessageService#%p response for %" PRId64 " is error: %d: %@]", self, _currentMessageId, (int)rpcError.errorCode, rpcError.errorDescription);
}
//boolTrue#997275b5 = Bool;
if (signature == 0x997275b5U) {
success = true;
}
}
_completion(success);
}
}
}
-(void)complete {
_completion(true);
}
@end
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface MTBindingTempAuthKeyContext : NSObject
@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int32_t messageSeqNo;
@property (nonatomic, strong, readonly) id transactionId;
- (instancetype)initWithMessageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo transactionId:(id)transactionId;
@end
@@ -0,0 +1,17 @@
#import "MTBindingTempAuthKeyContext.h"
@implementation MTBindingTempAuthKeyContext
- (instancetype)initWithMessageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo transactionId:(id)transactionId
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_messageSeqNo = messageSeqNo;
_transactionId = transactionId;
}
return self;
}
@end
+18
View File
@@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface MTBuffer : NSObject
- (void)appendInt32:(int32_t)value;
- (void)appendInt64:(int64_t)value;
- (void)appendBytes:(void const *)bytes length:(NSUInteger)length;
- (NSData *)data;
@end
@interface MTBuffer (TL)
- (void)appendTLBytes:(NSData *)bytes;
- (void)appendTLString:(NSString *)string;
@end
+90
View File
@@ -0,0 +1,90 @@
#import "MTBuffer.h"
@interface MTBuffer ()
{
NSMutableData *_data;
}
@end
@implementation MTBuffer
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_data = [[NSMutableData alloc] init];
}
return self;
}
- (void)appendInt32:(int32_t)value
{
[_data appendBytes:&value length:4];
}
- (void)appendInt64:(int64_t)value
{
[_data appendBytes:&value length:8];
}
- (void)appendBytes:(void const *)bytes length:(NSUInteger)length
{
[_data appendBytes:bytes length:length];
}
- (NSData *)data
{
return [[NSData alloc] initWithData:_data];
}
@end
static inline int roundUp(int numToRound, int multiple)
{
return multiple == 0 ? numToRound : ((numToRound % multiple) == 0 ? numToRound : (numToRound + multiple - (numToRound % multiple)));
}
@implementation MTBuffer (TL)
- (void)appendTLBytes:(NSData *)bytes
{
int32_t length = (int32_t)bytes.length;
if (bytes == nil || length == 0)
{
[self appendInt32:0];
return;
}
int paddingBytes = 0;
if (length >= 254)
{
uint8_t tmp = 254;
[self appendBytes:&tmp length:1];
[self appendBytes:(const uint8_t *)&length length:3];
paddingBytes = roundUp(length, 4) - length;
}
else
{
[self appendBytes:(const uint8_t *)&length length:1];
paddingBytes = roundUp(length + 1, 4) - (length + 1);
}
[self appendBytes:bytes.bytes length:length];
uint8_t tmp = 0;
for (int i = 0; i < paddingBytes; i++)
[self appendBytes:&tmp length:1];
}
- (void)appendTLString:(NSString *)string
{
[self appendTLBytes:[string dataUsingEncoding:NSUTF8StringEncoding]];
}
@end
@@ -0,0 +1,19 @@
#import "MTBuffer.h"
@interface MTBufferReader : NSObject
- (instancetype)initWithData:(NSData *)data;
- (bool)readBytes:(void *)bytes length:(NSUInteger)length;
- (bool)readInt32:(int32_t *)value;
- (bool)readInt64:(int64_t *)value;
- (NSData *)readRest;
@end
@interface MTBufferReader (TL)
- (bool)readTLString:(__autoreleasing NSString **)value;
- (bool)readTLBytes:(__autoreleasing NSData **)value;
@end
@@ -0,0 +1,126 @@
#import "MTBufferReader.h"
@interface MTBufferReader ()
{
NSData *_data;
NSUInteger _offset;
}
@end
@implementation MTBufferReader
- (instancetype)initWithData:(NSData *)data
{
self = [super init];
if (self != nil)
{
_data = data;
}
return self;
}
- (bool)readBytes:(void *)bytes length:(NSUInteger)length
{
if (_offset + length > _data.length)
return false;
if (bytes != NULL)
memcpy(bytes, _data.bytes + _offset, length);
_offset += length;
return true;
}
- (bool)readInt32:(int32_t *)value
{
return [self readBytes:value length:4];
}
- (bool)readInt64:(int64_t *)value
{
return [self readBytes:value length:8];
}
- (NSData *)readRest
{
return [_data subdataWithRange:NSMakeRange(_offset, _data.length - _offset)];
}
@end
static inline int roundUpInput(int32_t numToRound, int32_t multiple)
{
if (multiple == 0)
{
return numToRound;
}
int remainder = numToRound % multiple;
if (remainder == 0)
{
return numToRound;
}
return numToRound + multiple - remainder;
}
@implementation MTBufferReader (TL)
- (bool)readTLString:(__autoreleasing NSString **)value
{
NSData *bytes = nil;
if ([self readTLBytes:&bytes])
{
if (value)
*value = [[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding];
return true;
}
return false;
}
- (bool)readTLBytes:(__autoreleasing NSData **)value
{
uint8_t tmp = 0;
if ([self readBytes:&tmp length:1])
{
NSUInteger paddingBytes = 0;
int32_t length = tmp;
if (length == 254)
{
length = 0;
if (![self readBytes:((uint8_t *)&length) + 1 length:3])
return false;
length >>= 8;
paddingBytes = roundUpInput(length, 4) - length;
}
else
{
paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
}
uint8_t *bytes = (uint8_t *)malloc(length);
if (![self readBytes:bytes length:length])
return false;
NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true];
for (int i = 0; i < paddingBytes; i++)
{
if (![self readBytes:&tmp length:1])
return false;
}
if (value)
*value = result;
return true;
}
return false;
}
@end
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@class MTSignal;
@class MTContext;
@class MTSocksProxySettings;
@interface MTConnectionProbing : NSObject
+ (MTSignal *)probeProxyWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId settings:(MTSocksProxySettings *)settings;
@end
@@ -0,0 +1,143 @@
#import "MTConnectionProbing.h"
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTAtomic.h>
#import <MtProtoKit/MTHttpRequestOperation.h>
#import <MtProtoKit/MTEncryption.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTRequest.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTDatacenterAddress.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTProxyConnectivity.h>
#import "PingFoundation.h"
@interface MTPingHelper : NSObject <PingFoundationDelegate> {
void (^_success)();
PingFoundation *_ping;
}
@end
@implementation MTPingHelper
+ (void)runLoopThreadFunc {
while (true) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
}
+ (NSThread *)runLoopThread {
static NSThread *thread = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
thread = [[NSThread alloc] initWithTarget:self selector:@selector(runLoopThreadFunc) object:nil];
thread.name = @"MTPingHelper";
[thread start];
});
return thread;
}
+ (void)dispatchOnRunLoopThreadImpl:(void (^)())f {
if (f) {
f();
}
}
+ (void)dispatchOnRunLoopThread:(void (^)())block {
[self performSelector:@selector(dispatchOnRunLoopThreadImpl:) onThread:[self runLoopThread] withObject:[block copy] waitUntilDone:false];
}
- (instancetype)initWithSuccess:(void (^)())success {
self = [super init];
if (self != nil) {
_success = [success copy];
NSArray *hosts = @[
@"google.com",
@"8.8.8.8"
];
NSString *host = hosts[(int)(arc4random_uniform((uint32_t)hosts.count))];
_ping = [[PingFoundation alloc] initWithHostName:host];
_ping.delegate = self;
[_ping start];
}
return self;
}
- (void)dealloc {
#if DEBUG
assert(_ping.delegate == nil);
#endif
if (_ping.delegate != nil) {
_ping.delegate = nil;
[_ping stop];
}
}
- (void)stop {
_ping.delegate = nil;
[_ping stop];
}
- (void)pingFoundation:(PingFoundation *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber {
if (_success) {
_success();
}
}
- (void)pingFoundation:(PingFoundation *)pinger didStartWithAddress:(NSData *)__unused address {
[pinger sendPingWithData:nil];
}
@end
@implementation MTConnectionProbing
+ (MTSignal *)pingAddress {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
[MTPingHelper dispatchOnRunLoopThread:^{
MTPingHelper *helper = [[MTPingHelper alloc] initWithSuccess:^{
[subscriber putNext:@true];
[subscriber putCompletion];
}];
[disposable setDisposable:[[MTBlockDisposable alloc] initWithBlock:^{
[MTPingHelper dispatchOnRunLoopThread:^{
[helper stop];
}];
}]];
}];
return disposable;
}];
}
+ (MTSignal *)probeProxyWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId settings:(MTSocksProxySettings *)settings {
MTSignal *proxyAvailable = [[[MTProxyConnectivity pingProxyWithContext:context datacenterId:datacenterId settings:settings] map:^id(MTProxyConnectivityStatus *status) {
return @(status.reachable);
}] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal single:@false]];
MTSignal *referenceAvailable = [[self pingAddress] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal single:@false]];
MTSignal *combined = [[MTSignal combineSignals:@[proxyAvailable, referenceAvailable]] map:^id(NSArray *values) {
NSNumber *proxy = values[0];
NSNumber *ping = values[1];
if (![proxy boolValue] && [ping boolValue]) {
return @true;
} else {
return @false;
}
}];
return combined;
}
@end
File diff suppressed because it is too large Load Diff
+11
View File
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@class MTSignal;
@interface MTDNS : NSObject
+ (MTSignal *)resolveHostname:(NSString *)hostname;
+ (MTSignal *)resolveHostnameNative:(NSString *)hostname port:(int32_t)port;
+ (MTSignal *)resolveHostnameUniversal:(NSString *)hostname port:(int32_t)port;
@end
+340
View File
@@ -0,0 +1,340 @@
#import "MTDNS.h"
#import <arpa/inet.h>
#include <netinet/tcp.h>
#import <fcntl.h>
#import <ifaddrs.h>
#import <netdb.h>
#import <netinet/in.h>
#import <net/if.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTBag.h>
#import <MtProtoKit/MTAtomic.h>
#import <MtProtoKit/MTHttpRequestOperation.h>
#import <MtProtoKit/MTEncryption.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTRequest.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTDatacenterAddress.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTLogging.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@interface MTDNSHostContext : NSObject {
MTBag *_subscribers;
id<MTDisposable> _disposable;
}
@end
@implementation MTDNSHostContext
- (instancetype)initWithHost:(NSString *)host disposable:(id<MTDisposable>)disposable {
self = [super init];
if (self != nil) {
_subscribers = [[MTBag alloc] init];
_disposable = disposable;
}
return self;
}
- (void)dealloc {
[_disposable dispose];
}
- (NSInteger)addSubscriber:(void (^)(NSString *))completion {
return [_subscribers addItem:[completion copy]];
}
- (void)removeSubscriber:(NSInteger)index {
[_subscribers removeItem:index];
}
- (bool)isEmpty {
return [_subscribers isEmpty];
}
- (void)complete:(NSString *)result {
for (void (^completion)(NSString *) in [_subscribers copyItems]) {
completion(result);
}
}
@end
@interface MTDNSContext : NSObject {
NSMutableDictionary<NSString *, MTDNSHostContext *> *_contexts;
}
@end
@implementation MTDNSContext
+ (MTQueue *)sharedQueue {
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = [[MTQueue alloc] init];
});
return queue;
}
+ (MTSignal *)shared {
return [[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
static MTDNSContext *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MTDNSContext alloc] init];
});
[subscriber putNext:instance];
[subscriber putCompletion];
return nil;
}] startOn:[self sharedQueue]];
}
- (instancetype)init {
self = [super init];
if (self != nil) {
_contexts = [[NSMutableDictionary alloc] init];
}
return self;
}
- (id<MTDisposable>)subscribe:(NSString *)host port:(int32_t)port completion:(void (^)(NSString *))completion {
NSString *key = [NSString stringWithFormat:@"%@:%d", host, port];
MTMetaDisposable *disposable = nil;
if (_contexts[key] == nil) {
disposable = [[MTMetaDisposable alloc] init];
_contexts[key] = [[MTDNSHostContext alloc] initWithHost:host disposable:disposable];
}
MTDNSHostContext *context = _contexts[key];
NSInteger index = [context addSubscriber:^(NSString *result) {
if (completion) {
completion(result);
}
}];
if (disposable != nil) {
__weak MTDNSContext *weakSelf = self;
[disposable setDisposable:[[[self performLookup:host port:port] deliverOn:[MTDNSContext sharedQueue]] startWithNextStrict:^(NSString *result) {
__strong MTDNSContext *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
if (strongSelf->_contexts[key] != nil) {
[strongSelf->_contexts[key] complete:result];
[strongSelf->_contexts removeObjectForKey:key];
}
} file:__FILE_NAME__ line:__LINE__]];
}
__weak MTDNSContext *weakSelf = self;
__weak MTDNSHostContext *weakContext = context;
return [[MTBlockDisposable alloc] initWithBlock:^{
[[MTDNSContext sharedQueue] dispatchOnQueue:^{
__strong MTDNSContext *strongSelf = weakSelf;
__strong MTDNSHostContext *strongContext = weakContext;
if (strongSelf == nil || strongContext == nil) {
return;
}
if (strongSelf->_contexts[key] != nil && strongSelf->_contexts[key] == strongContext) {
[strongSelf->_contexts[key] removeSubscriber:index];
if ([strongSelf->_contexts[key] isEmpty]) {
[strongSelf->_contexts removeObjectForKey:key];
}
}
}];
}];
}
- (MTSignal *)performLookup:(NSString *)host port:(int32_t)port {
MTSignal *lookupOnce = [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
[[MTQueue concurrentDefaultQueue] dispatchOnQueue:^{
struct addrinfo hints, *res, *res0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
NSString *portStr = [NSString stringWithFormat:@"%d", port];
if (MTLogEnabled()) {
MTLog(@"[MTDNS lookup %@:%@]", host, portStr);
}
int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
NSString *address4 = nil;
NSString *address6 = nil;
if (gai_error == 0) {
for(res = res0; res; res = res->ai_next) {
if ((address4 == nil) && (res->ai_family == AF_INET)) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr;
char *s = malloc(INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
address4 = [NSString stringWithUTF8String:s];
free(s);
} else if ((address6 == nil) && (res->ai_family == AF_INET6)) {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
char *s = malloc(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
address6 = [NSString stringWithUTF8String:s];
free(s);
}
}
freeaddrinfo(res0);
}
if (address4 != nil) {
if (MTLogEnabled()) {
MTLog(@"[MTDNS lookup %@:%@ success ipv4]", host, portStr);
}
[subscriber putNext:address4];
[subscriber putCompletion];
} else if (address6 != nil) {
if (MTLogEnabled()) {
MTLog(@"[MTDNS lookup %@:%@ success ipv6]", host, portStr);
}
[subscriber putNext:address6];
[subscriber putCompletion];
} else {
if (MTLogEnabled()) {
MTLog(@"[MTDNS lookup %@:%@ error %d]", host, portStr, gai_error);
}
[subscriber putError:nil];
}
}];
return disposable;
}];
return [[[lookupOnce catch:^MTSignal *(__unused id error) {
return [[MTSignal complete] delay:2.0 onQueue:[MTDNSContext sharedQueue]];
}] restart] take:1];
}
@end
@interface MTDNSCachedHostname : NSObject
@property (nonatomic, strong) NSString *ip;
@property (nonatomic) NSTimeInterval timestamp;
@end
@implementation MTDNSCachedHostname
- (instancetype)initWithIp:(NSString *)ip timestamp:(NSTimeInterval)timestamp {
self = [super init];
if (self != nil) {
_ip = ip;
_timestamp = timestamp;
}
return self;
}
@end
@implementation MTDNS
+ (MTAtomic *)hostnameCache {
static MTAtomic *result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
result = [[MTAtomic alloc] initWithValue:[[NSMutableDictionary alloc] init]];
});
return result;
}
+ (NSString *)cachedIp:(NSString *)hostname {
return [[self hostnameCache] with:^id (NSMutableDictionary *dict) {
MTDNSCachedHostname *result = dict[hostname];
if (result != nil && result.timestamp > CFAbsoluteTimeGetCurrent() - 10.0 * 60.0) {
return result.ip;
}
return nil;
}];
}
+ (void)cacheIp:(NSString *)hostname ip:(NSString *)ip {
[[self hostnameCache] with:^id (NSMutableDictionary *dict) {
dict[hostname] = [[MTDNSCachedHostname alloc] initWithIp:ip timestamp:CFAbsoluteTimeGetCurrent()];
return nil;
}];
}
+ (MTSignal *)resolveHostname:(NSString *)hostname {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
NSString *cached = [self cachedIp:hostname];
if (cached != nil) {
[subscriber putNext:cached];
[subscriber putCompletion];
return nil;
}
NSDictionary *headers = @{@"Host": @"dns.google.com"};
return [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://google.com/resolve?name=%@", hostname]] headers:headers] mapToSignal:^MTSignal *(MTHttpResponse *response) {
NSData *data = response.data;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([dict respondsToSelector:@selector(objectForKey:)]) {
NSArray *answer = dict[@"Answer"];
if ([answer respondsToSelector:@selector(objectAtIndex:)]) {
for (NSDictionary *item in answer) {
if ([item respondsToSelector:@selector(objectForKey:)]) {
NSString *itemData = item[@"data"];
if ([itemData respondsToSelector:@selector(characterAtIndex:)]) {
bool isIp = true;
struct in_addr ip4;
struct in6_addr ip6;
if (inet_aton(itemData.UTF8String, &ip4) == 0) {
if (inet_pton(AF_INET6, itemData.UTF8String, &ip6) == 0) {
isIp = false;
}
}
if (isIp) {
[self cacheIp:hostname ip:itemData];
return [MTSignal single:itemData];
}
}
}
}
}
}
[subscriber putNext:hostname];
[subscriber putCompletion];
return nil;
}] startWithNext:^(id next) {
[subscriber putNext:next];
[subscriber putCompletion];
} error:^(id error) {
[subscriber putNext:hostname];
[subscriber putCompletion];
} completed:nil];
}];
}
+ (MTSignal *)resolveHostnameNative:(NSString *)hostname port:(int32_t)port {
return [[MTDNSContext shared] mapToSignal:^MTSignal *(MTDNSContext *context) {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
return [context subscribe:hostname port:port completion:^(NSString *result) {
[subscriber putNext:result];
[subscriber putCompletion];
}];
}];
}];
}
+ (MTSignal *)resolveHostnameUniversal:(NSString *)hostname port:(int32_t)port {
return [[self resolveHostname:hostname] timeout:10.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[self resolveHostnameNative:hostname port:port]];
}
@end
@@ -0,0 +1,122 @@
#import <MtProtoKit/MTDatacenterAddress.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@implementation MTDatacenterAddress
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData *)secret
{
self = [super init];
if (self != nil)
{
_ip = ip;
_port = port;
_preferForMedia = preferForMedia;
_restrictToTcp = restrictToTcp;
_cdn = cdn;
_preferForProxy = preferForProxy;
_secret = secret;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_ip = [aDecoder decodeObjectForKey:@"ip"];
_host = [aDecoder decodeObjectForKey:@"host"];
_port = (uint16_t)[aDecoder decodeIntForKey:@"port"];
_preferForMedia = [aDecoder decodeBoolForKey:@"preferForMedia"];
_restrictToTcp = [aDecoder decodeBoolForKey:@"restrictToTcp"];
_cdn = [aDecoder decodeBoolForKey:@"cdn"];
_preferForProxy = [aDecoder decodeBoolForKey:@"preferForProxy"];
_secret = [aDecoder decodeObjectForKey:@"secret"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_ip forKey:@"ip"];
[aCoder encodeObject:_host forKey:@"host"];
[aCoder encodeInt:_port forKey:@"port"];
[aCoder encodeBool:_preferForMedia forKey:@"preferForMedia"];
[aCoder encodeBool:_restrictToTcp forKey:@"restrictToTcp"];
[aCoder encodeBool:_cdn forKey:@"cdn"];
[aCoder encodeBool:_preferForProxy forKey:@"preferForProxy"];
[aCoder encodeObject:_secret forKey:@"secret"];
}
- (instancetype)copyWithZone:(NSZone *)__unused zone {
return self;
}
- (BOOL)isEqual:(id)object
{
if (![object isKindOfClass:[MTDatacenterAddress class]])
return false;
return [self isEqualToAddress:object];
}
- (NSUInteger)hash {
return [_ip hash] * 31 + ((NSUInteger)_port);
}
- (BOOL)isEqualToAddress:(MTDatacenterAddress *)other
{
if (![other isKindOfClass:[MTDatacenterAddress class]])
return false;
if (![_ip isEqualToString:other.ip])
return false;
if (_port != other.port)
return false;
if (_preferForMedia != other.preferForMedia)
return false;
if (_restrictToTcp != other.restrictToTcp) {
return false;
}
if (_cdn != other.cdn) {
return false;
}
if (_preferForProxy != other.preferForProxy) {
return false;
}
if ((_secret != nil) && (other->_secret != nil)) {
if (![_secret isEqualToData:other->_secret]) {
return false;
}
} else if ((_secret != nil) != (other->_secret != nil)) {
return false;
}
return true;
}
- (BOOL)isIpv6
{
const char *utf8 = [_ip UTF8String];
int success;
struct in6_addr dst6;
success = inet_pton(AF_INET6, utf8, &dst6);
return success == 1;
}
- (NSString *)description
{
return [[NSString alloc] initWithFormat:@"%@:%d (media: %@, cdn: %@, static: %@, secret: %@)", _ip == nil ? _host : _ip, (int)_port, _preferForMedia ? @"yes" : @"no", _cdn ? @"yes" : @"no", _preferForProxy ? @"yes" : @"no", _secret];
}
@end
@@ -0,0 +1,19 @@
#import <MtProtoKit/MTDatacenterAddressListData.h>
@implementation MTDatacenterAddressListData
- (instancetype)initWithAddressList:(NSDictionary<NSNumber *, NSArray *> *)addressList
{
self = [super init];
if (self != nil)
{
_addressList = addressList;
}
return self;
}
- (NSString *)description {
return _addressList.description;
}
@end
@@ -0,0 +1,69 @@
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTDatacenterAddress.h>
@implementation MTDatacenterAddressSet
- (instancetype)initWithAddressList:(NSArray *)addressList
{
self = [super init];
if (self != nil)
{
_addressList = addressList;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_addressList = [aDecoder decodeObjectForKey:@"addressList"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_addressList forKey:@"addressList"];
}
- (BOOL)isEqual:(MTDatacenterAddressSet *)another
{
if (![another isKindOfClass:[MTDatacenterAddressSet class]])
return false;
if (_addressList.count != another.addressList.count)
return false;
for (NSUInteger i = 0; i < _addressList.count; i++)
{
if (![_addressList[i] isEqual:another.addressList[i]])
return false;
}
return true;
}
- (NSString *)description
{
NSMutableString *string = [[NSMutableString alloc] init];
[string appendString:@"["];
for (MTDatacenterAddress *address in _addressList)
{
if (string.length != 1)
[string appendString:@", "];
[string appendString:[address description]];
}
[string appendString:@"]"];
return string;
}
- (MTDatacenterAddress *)firstAddress
{
return _addressList.count == 0 ? nil : _addressList[0];
}
@end
@@ -0,0 +1,211 @@
#import <MtProtoKit/MTDatacenterAuthAction.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTRequest.h>
#import <MtProtoKit/MTDatacenterSaltInfo.h>
#import <MtProtoKit/MTDatacenterAuthInfo.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTDatacenterAuthMessageService.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTBindKeyMessageService.h>
#import "MTBuffer.h"
@interface MTDatacenterAuthAction () <MTDatacenterAuthMessageServiceDelegate>
{
void (^_completion)(MTDatacenterAuthAction *, bool);
bool _isCdn;
bool _skipBind;
MTDatacenterAuthInfoSelector _authKeyInfoSelector;
NSInteger _datacenterId;
__weak MTContext *_context;
bool _awaitingAddresSetUpdate;
MTProto *_authMtProto;
MTProto *_bindMtProto;
}
@end
@implementation MTDatacenterAuthAction
- (instancetype)initWithAuthKeyInfoSelector:(MTDatacenterAuthInfoSelector)authKeyInfoSelector isCdn:(bool)isCdn skipBind:(bool)skipBind completion:(void (^)(MTDatacenterAuthAction *, bool))completion {
self = [super init];
if (self != nil) {
_authKeyInfoSelector = authKeyInfoSelector;
_isCdn = isCdn;
_skipBind = skipBind;
_completion = [completion copy];
}
return self;
}
- (void)dealloc {
[self cleanup];
}
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId {
_datacenterId = datacenterId;
_context = context;
if (_datacenterId != 0 && context != nil)
{
bool alreadyCompleted = false;
MTDatacenterAuthInfo *currentAuthInfo = [context authInfoForDatacenterWithId:_datacenterId selector:_authKeyInfoSelector];
if (currentAuthInfo != nil) {
alreadyCompleted = true;
}
if (alreadyCompleted) {
[self complete];
} else {
_authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
_authMtProto.cdn = _isCdn;
_authMtProto.useUnauthorizedMode = true;
bool tempAuth = false;
switch (_authKeyInfoSelector) {
case MTDatacenterAuthInfoSelectorEphemeralMain:
tempAuth = true;
_authMtProto.media = false;
break;
case MTDatacenterAuthInfoSelectorEphemeralMedia:
tempAuth = true;
_authMtProto.media = true;
_authMtProto.enforceMedia = true;
break;
default:
break;
}
MTDatacenterAuthMessageService *authService = [[MTDatacenterAuthMessageService alloc] initWithContext:context tempAuth:tempAuth];
authService.delegate = self;
[_authMtProto addMessageService:authService];
[_authMtProto resume];
}
}
else
[self fail];
}
- (void)authMessageServiceCompletedWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp {
[self completeWithAuthKey:authKey timestamp:timestamp];
}
- (void)completeWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp {
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthAction#%p@%p: completeWithAuthKey %lld selector %d]", self, _context, authKey.authKeyId, _authKeyInfoSelector);
}
switch (_authKeyInfoSelector) {
case MTDatacenterAuthInfoSelectorPersistent: {
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId validUntilTimestamp:INT32_MAX saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil];
MTContext *context = _context;
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo selector:_authKeyInfoSelector];
[self complete];
}
break;
case MTDatacenterAuthInfoSelectorEphemeralMain:
case MTDatacenterAuthInfoSelectorEphemeralMedia: {
MTContext *mainContext = _context;
if (mainContext != nil) {
if (_skipBind) {
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId validUntilTimestamp:authKey.validUntilTimestamp saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil];
[_context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo selector:_authKeyInfoSelector];
[self complete];
} else {
MTDatacenterAuthInfo *persistentAuthInfo = [mainContext authInfoForDatacenterWithId:_datacenterId selector:MTDatacenterAuthInfoSelectorPersistent];
if (persistentAuthInfo != nil) {
_bindMtProto = [[MTProto alloc] initWithContext:mainContext datacenterId:_datacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
_bindMtProto.cdn = false;
_bindMtProto.useUnauthorizedMode = false;
_bindMtProto.useTempAuthKeys = true;
_bindMtProto.useExplicitAuthKey = authKey;
_bindMtProto.tempConnectionForReuse = [_authMtProto takeConnectionForReusing];
switch (_authKeyInfoSelector) {
case MTDatacenterAuthInfoSelectorEphemeralMain:
_bindMtProto.media = false;
break;
case MTDatacenterAuthInfoSelectorEphemeralMedia:
_bindMtProto.media = true;
_bindMtProto.enforceMedia = true;
break;
default:
break;
}
__weak MTDatacenterAuthAction *weakSelf = self;
[_bindMtProto addMessageService:[[MTBindKeyMessageService alloc] initWithPersistentKey:[[MTDatacenterAuthKey alloc] initWithAuthKey:persistentAuthInfo.authKey authKeyId:persistentAuthInfo.authKeyId validUntilTimestamp:persistentAuthInfo.validUntilTimestamp notBound:false] ephemeralKey:authKey completion:^(bool success) {
__strong MTDatacenterAuthAction *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
[strongSelf->_bindMtProto stop];
if (success) {
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId validUntilTimestamp:authKey.validUntilTimestamp saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil];
[strongSelf->_context updateAuthInfoForDatacenterWithId:strongSelf->_datacenterId authInfo:authInfo selector:strongSelf->_authKeyInfoSelector];
[strongSelf complete];
} else {
[strongSelf fail];
}
}]];
[_bindMtProto resume];
}
}
}
}
break;
default:
assert(false);
break;
}
}
- (void)cleanup
{
MTProto *authMtProto = _authMtProto;
_authMtProto = nil;
[authMtProto stop];
MTProto *bindMtProto = _bindMtProto;
_bindMtProto = nil;
[bindMtProto stop];
}
- (void)cancel
{
[self cleanup];
}
- (void)complete {
if (_completion) {
_completion(self, true);
}
}
- (void)fail
{
if (_completion) {
_completion(self, false);
}
}
@end
@@ -0,0 +1,138 @@
#import <MtProtoKit/MTDatacenterAuthInfo.h>
#import <MtProtoKit/MTDatacenterSaltInfo.h>
@implementation MTDatacenterAuthKey
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId validUntilTimestamp:(int32_t)validUntilTimestamp notBound:(bool)notBound {
self = [super init];
if (self != nil) {
_authKey = authKey;
_authKeyId = authKeyId;
_validUntilTimestamp = validUntilTimestamp;
_notBound = notBound;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
int32_t validUntilTimestamp = [aDecoder decodeInt32ForKey:@"validUntilTimestamp"];
if (validUntilTimestamp == 0) {
validUntilTimestamp = INT32_MAX;
}
return [self initWithAuthKey:[aDecoder decodeObjectForKey:@"key"] authKeyId:[aDecoder decodeInt64ForKey:@"keyId"] validUntilTimestamp:validUntilTimestamp notBound:[aDecoder decodeBoolForKey:@"notBound"]];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_authKey forKey:@"key"];
[aCoder encodeInt64:_authKeyId forKey:@"keyId"];
[aCoder encodeInt32:_validUntilTimestamp forKey:@"validUntilTimestamp"];
[aCoder encodeBool:_notBound forKey:@"notBound"];
}
@end
@implementation MTDatacenterAuthInfo
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId validUntilTimestamp:(int32_t)validUntilTimestamp saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes
{
self = [super init];
if (self != nil)
{
_authKey = authKey;
_authKeyId = authKeyId;
_saltSet = saltSet;
_validUntilTimestamp = validUntilTimestamp;
_authKeyAttributes = authKeyAttributes;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_authKey = [aDecoder decodeObjectForKey:@"authKey"];
_authKeyId = [aDecoder decodeInt64ForKey:@"authKeyId"];
int32_t validUntilTimestamp = [aDecoder decodeInt32ForKey:@"validUntilTimestamp"];
_validUntilTimestamp = validUntilTimestamp;
_saltSet = [aDecoder decodeObjectForKey:@"saltSet"];
_authKeyAttributes = [aDecoder decodeObjectForKey:@"authKeyAttributes"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_authKey forKey:@"authKey"];
[aCoder encodeInt64:_authKeyId forKey:@"authKeyId"];
[aCoder encodeInt32:_validUntilTimestamp forKey:@"validUntilTimestamp"];
[aCoder encodeObject:_saltSet forKey:@"saltSet"];
[aCoder encodeObject:_authKeyAttributes forKey:@"authKeyAttributes"];
}
- (int64_t)authSaltForMessageId:(int64_t)messageId
{
int64_t bestSalt = 0;
int64_t bestValidMessageCount = 0;
for (MTDatacenterSaltInfo *saltInfo in _saltSet)
{
int64_t currentValidMessageCount = [saltInfo validMessageCountAfterId:messageId];
if (currentValidMessageCount != 0 && currentValidMessageCount > bestValidMessageCount)
bestSalt = saltInfo.salt;
}
return bestSalt;
}
- (MTDatacenterAuthInfo *)mergeSaltSet:(NSArray *)updatedSaltSet forTimestamp:(NSTimeInterval)timestamp
{
int64_t referenceMessageId = (int64_t)(timestamp * 4294967296);
NSMutableArray *mergedSaltSet = [[NSMutableArray alloc] init];
for (MTDatacenterSaltInfo *saltInfo in _saltSet)
{
if ([saltInfo isValidFutureSaltForMessageId:referenceMessageId])
[mergedSaltSet addObject:saltInfo];
}
for (MTDatacenterSaltInfo *saltInfo in updatedSaltSet)
{
bool alreadExists = false;
for (MTDatacenterSaltInfo *existingSaltInfo in mergedSaltSet)
{
if (existingSaltInfo.firstValidMessageId == saltInfo.firstValidMessageId)
{
alreadExists = true;
break;
}
}
if (!alreadExists)
{
if ([saltInfo isValidFutureSaltForMessageId:referenceMessageId])
[mergedSaltSet addObject:saltInfo];
}
}
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId validUntilTimestamp:_validUntilTimestamp saltSet:mergedSaltSet authKeyAttributes:_authKeyAttributes];
}
- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes {
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId validUntilTimestamp:_validUntilTimestamp saltSet:_saltSet authKeyAttributes:authKeyAttributes];
}
- (MTDatacenterAuthKey *)persistentAuthKey {
return [[MTDatacenterAuthKey alloc] initWithAuthKey:_authKey authKeyId:_authKeyId validUntilTimestamp:_validUntilTimestamp notBound:false];
}
- (NSString *)description {
return [NSString stringWithFormat:@"MTDatacenterAuthInfo authKeyId:%" PRId64 " authKey:%lu", _authKeyId, (unsigned long)_authKey.length];
}
@end
@@ -0,0 +1,866 @@
#import <MtProtoKit/MTDatacenterAuthMessageService.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTSessionInfo.h>
#import <MtProtoKit/MTIncomingMessage.h>
#import <MtProtoKit/MTOutgoingMessage.h>
#import <MtProtoKit/MTMessageTransaction.h>
#import <MtProtoKit/MTPreparedMessage.h>
#import <MtProtoKit/MTDatacenterAuthInfo.h>
#import <MtProtoKit/MTDatacenterSaltInfo.h>
#import "MTBuffer.h"
#import <MtProtoKit/MTEncryption.h>
#import <CommonCrypto/CommonCrypto.h>
#import "MTInternalMessageParser.h"
#import "MTServerDhInnerDataMessage.h"
#import "MTResPqMessage.h"
#import "MTServerDhParamsMessage.h"
#import "MTSetClientDhParamsResponseMessage.h"
@interface MTDatacenterAuthPublicKey : NSObject
@property (nonatomic, strong, readonly) NSString *publicKey;
@end
@implementation MTDatacenterAuthPublicKey
- (instancetype)initWithPublicKey:(NSString *)publicKey {
self = [super init];
if (self != nil) {
_publicKey = publicKey;
}
return self;
}
- (uint64_t)fingerprintWithEncryptionProvider:(id<EncryptionProvider>)encryptionProvider {
return MTRsaFingerprint(encryptionProvider, _publicKey);
}
@end
static NSArray<MTDatacenterAuthPublicKey *> *defaultPublicKeys(bool isProduction) {
static NSArray<MTDatacenterAuthPublicKey *> *testingPublicKeys = nil;
static NSArray<MTDatacenterAuthPublicKey *> *productionPublicKeys = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
testingPublicKeys = @[
[[MTDatacenterAuthPublicKey alloc] initWithPublicKey:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAyMEdY1aR+sCR3ZSJrtztKTKqigvO/vBfqACJLZtS7QMgCGXJ6XIR\n"
"yy7mx66W0/sOFa7/1mAZtEoIokDP3ShoqF4fVNb6XeqgQfaUHd8wJpDWHcR2OFwv\n"
"plUUI1PLTktZ9uW2WE23b+ixNwJjJGwBDJPQEQFBE+vfmH0JP503wr5INS1poWg/\n"
"j25sIWeYPHYeOrFp/eXaqhISP6G+q2IeTaWTXpwZj4LzXq5YOpk4bYEQ6mvRq7D1\n"
"aHWfYmlEGepfaYR8Q0YqvvhYtMte3ITnuSJs171+GDqpdKcSwHnd6FudwGO4pcCO\n"
"j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB\n"
"-----END RSA PUBLIC KEY-----"]
];
productionPublicKeys = @[
[[MTDatacenterAuthPublicKey alloc] initWithPublicKey:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEA6LszBcC1LGzyr992NzE0ieY+BSaOW622Aa9Bd4ZHLl+TuFQ4lo4g\n"
"5nKaMBwK/BIb9xUfg0Q29/2mgIR6Zr9krM7HjuIcCzFvDtr+L0GQjae9H0pRB2OO\n"
"62cECs5HKhT5DZ98K33vmWiLowc621dQuwKWSQKjWf50XYFw42h21P2KXUGyp2y/\n"
"+aEyZ+uVgLLQbRA1dEjSDZ2iGRy12Mk5gpYc397aYp438fsJoHIgJ2lgMv5h7WY9\n"
"t6N/byY9Nw9p21Og3AoXSL2q/2IJ1WRUhebgAdGVMlV1fkuOQoEzR7EdpqtQD9Cs\n"
"5+bfo3Nhmcyvk5ftB0WkJ9z6bNZ7yxrP8wIDAQAB\n"
"-----END RSA PUBLIC KEY-----"]
];
});
if (isProduction) {
return productionPublicKeys;
} else {
return testingPublicKeys;
}
}
static MTDatacenterAuthPublicKey *selectPublicKey(id<EncryptionProvider> encryptionProvider, NSArray<NSNumber *> *fingerprints, NSArray<MTDatacenterAuthPublicKey *> *publicKeys) {
for (NSNumber *nFingerprint in fingerprints) {
for (MTDatacenterAuthPublicKey *key in publicKeys) {
uint64_t keyFingerprint = [key fingerprintWithEncryptionProvider:encryptionProvider];
if ([nFingerprint unsignedLongLongValue] == keyFingerprint) {
return key;
}
}
}
return nil;
}
typedef enum {
MTDatacenterAuthStageWaitingForPublicKeys = 0,
MTDatacenterAuthStagePQ = 1,
MTDatacenterAuthStageReqDH = 2,
MTDatacenterAuthStageKeyVerification = 3,
MTDatacenterAuthStageDone = 4
} MTDatacenterAuthStage;
@interface MTDatacenterAuthMessageService ()
{
id<EncryptionProvider> _encryptionProvider;
bool _tempAuth;
MTDatacenterAuthStage _stage;
int64_t _currentStageMessageId;
int32_t _currentStageMessageSeqNo;
id _currentStageTransactionId;
NSData *_nonce;
NSData *_serverNonce;
NSData *_newNonce;
NSData *_dhP;
NSData *_dhQ;
int64_t _dhPublicKeyFingerprint;
NSData *_dhEncryptedData;
MTDatacenterAuthKey *_authKey;
NSData *_encryptedClientData;
NSArray<MTDatacenterAuthPublicKey *> *_publicKeys;
}
@end
@implementation MTDatacenterAuthMessageService
- (instancetype)initWithContext:(MTContext *)context tempAuth:(bool)tempAuth
{
self = [super init];
if (self != nil)
{
_encryptionProvider = context.encryptionProvider;
_tempAuth = tempAuth;
}
return self;
}
- (NSArray<MTDatacenterAuthPublicKey *> *)convertPublicKeysFromDictionaries:(NSArray<NSDictionary *> *)list {
NSMutableArray<MTDatacenterAuthPublicKey *> *cdnKeys = [[NSMutableArray alloc] init];
for (NSDictionary *dict in list) {
NSString *key = dict[@"key"];
if ([key isKindOfClass:[NSString class]]) {
[cdnKeys addObject:[[MTDatacenterAuthPublicKey alloc] initWithPublicKey:key]];
}
}
return cdnKeys;
}
- (void)reset:(MTProto *)mtProto
{
_currentStageMessageId = 0;
_currentStageMessageSeqNo = 0;
_currentStageTransactionId = nil;
_nonce = nil;
_serverNonce = nil;
_newNonce = nil;
_dhP = nil;
_dhQ = nil;
_dhPublicKeyFingerprint = 0;
_dhEncryptedData = nil;
_authKey = nil;
_encryptedClientData = nil;
if (mtProto.cdn) {
_publicKeys = [self convertPublicKeysFromDictionaries:[mtProto.context publicKeysForDatacenterWithId:mtProto.datacenterId]];
if (_publicKeys.count == 0) {
_stage = MTDatacenterAuthStageWaitingForPublicKeys;
[mtProto.context publicKeysForDatacenterWithIdRequired:mtProto.datacenterId];
} else {
_stage = MTDatacenterAuthStagePQ;
}
} else {
_publicKeys = defaultPublicKeys(!mtProto.context.isTestingEnvironment);
_stage = MTDatacenterAuthStagePQ;
}
[mtProto requestSecureTransportReset];
[mtProto requestTransportTransaction];
}
- (void)mtProtoDidAddService:(MTProto *)mtProto
{
[self reset:mtProto];
}
- (void)mtProtoPublicKeysUpdated:(MTProto *)mtProto datacenterId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys {
if (!mtProto.cdn) {
return;
}
if (_stage == MTDatacenterAuthStageWaitingForPublicKeys) {
if (mtProto.datacenterId == datacenterId) {
_publicKeys = [self convertPublicKeysFromDictionaries:publicKeys];
if (_publicKeys != nil && _publicKeys.count != 0) {
_stage = MTDatacenterAuthStagePQ;
[mtProto requestTransportTransaction];
}
}
}
}
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo scheme:(MTTransportScheme *)scheme
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p mtProto#%p (media: %s) mtProtoMessageTransaction scheme:%@]", self, mtProto, mtProto.media ? "true" : "false", scheme);
}
if (_currentStageTransactionId == nil)
{
switch (_stage)
{
case MTDatacenterAuthStageWaitingForPublicKeys:
break;
case MTDatacenterAuthStagePQ:
{
if (_nonce == nil)
{
uint8_t nonceBytes[16];
__unused int result = SecRandomCopyBytes(kSecRandomDefault, 16, nonceBytes);
_nonce = [[NSData alloc] initWithBytes:nonceBytes length:16];
}
MTBuffer *reqPqBuffer = [[MTBuffer alloc] init];
[reqPqBuffer appendInt32:(int32_t)0xbe7e8ef1];
[reqPqBuffer appendBytes:_nonce.bytes length:_nonce.length];
NSString *messageDescription = [NSString stringWithFormat:@"reqPq nonce:%@", _nonce];
MTOutgoingMessage *message = [[MTOutgoingMessage alloc] initWithData:reqPqBuffer.data metadata:messageDescription additionalDebugDescription:nil shortMetadata:messageDescription messageId:_currentStageMessageId messageSeqNo:_currentStageMessageSeqNo];
return [[MTMessageTransaction alloc] initWithMessagePayload:@[message] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
if (_stage == MTDatacenterAuthStagePQ && messageInternalIdToTransactionId[message.internalId] != nil && messageInternalIdToPreparedMessage[message.internalId] != nil)
{
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[message.internalId];
_currentStageMessageId = preparedMessage.messageId;
_currentStageMessageSeqNo = preparedMessage.seqNo;
_currentStageTransactionId = messageInternalIdToTransactionId[message.internalId];
}
}];
}
case MTDatacenterAuthStageReqDH:
{
#if DEBUG
if (arc4random_uniform(100) < 50) {
[mtProto simulateDisconnection];
}
#endif
MTBuffer *reqDhBuffer = [[MTBuffer alloc] init];
[reqDhBuffer appendInt32:(int32_t)0xd712e4be];
[reqDhBuffer appendBytes:_nonce.bytes length:_nonce.length];
[reqDhBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
[reqDhBuffer appendTLBytes:_dhP];
[reqDhBuffer appendTLBytes:_dhQ];
[reqDhBuffer appendInt64:_dhPublicKeyFingerprint];
[reqDhBuffer appendTLBytes:_dhEncryptedData];
NSString *messageDescription = [NSString stringWithFormat:@"reqDh nonce:%@ serverNonce:%@ p:%@ q:%@ fingerprint:%llx dhEncryptedData:%d bytes", _nonce, _serverNonce, _dhP, _dhQ, _dhPublicKeyFingerprint, (int)_dhEncryptedData.length];
MTOutgoingMessage *message = [[MTOutgoingMessage alloc] initWithData:reqDhBuffer.data metadata:messageDescription additionalDebugDescription:nil shortMetadata:messageDescription messageId:_currentStageMessageId messageSeqNo:_currentStageMessageSeqNo];
return [[MTMessageTransaction alloc] initWithMessagePayload:@[message] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
if (_stage == MTDatacenterAuthStageReqDH && messageInternalIdToTransactionId[message.internalId] != nil && messageInternalIdToPreparedMessage[message.internalId] != nil)
{
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[message.internalId];
_currentStageMessageId = preparedMessage.messageId;
_currentStageMessageSeqNo = preparedMessage.seqNo;
_currentStageTransactionId = messageInternalIdToTransactionId[message.internalId];
}
}];
}
case MTDatacenterAuthStageKeyVerification:
{
MTBuffer *setDhParamsBuffer = [[MTBuffer alloc] init];
[setDhParamsBuffer appendInt32:(int32_t)0xf5045f1f];
[setDhParamsBuffer appendBytes:_nonce.bytes length:_nonce.length];
[setDhParamsBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
[setDhParamsBuffer appendTLBytes:_encryptedClientData];
MTOutgoingMessage *message = [[MTOutgoingMessage alloc] initWithData:setDhParamsBuffer.data metadata:@"setDhParams" additionalDebugDescription:nil shortMetadata:@"setDhParams" messageId:_currentStageMessageId messageSeqNo:_currentStageMessageSeqNo];
return [[MTMessageTransaction alloc] initWithMessagePayload:@[message] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
if (_stage == MTDatacenterAuthStageKeyVerification && messageInternalIdToTransactionId[message.internalId] != nil && messageInternalIdToPreparedMessage[message.internalId] != nil)
{
MTPreparedMessage *preparedMessage = messageInternalIdToPreparedMessage[message.internalId];
_currentStageMessageId = preparedMessage.messageId;
_currentStageMessageSeqNo = preparedMessage.seqNo;
_currentStageTransactionId = messageInternalIdToTransactionId[message.internalId];
}
}];
}
default:
break;
}
}
return nil;
}
static NSData *reversedBytes(NSData *data) {
NSMutableData *result = [[NSMutableData alloc] initWithLength:data.length];
for (NSUInteger i = 0; i < result.length; i++) {
((uint8_t *)result.mutableBytes)[i] = ((uint8_t *)data.bytes)[result.length - i - 1];
}
return result;
}
static NSData *encryptRSAModernPadding(id<EncryptionProvider> encryptionProvider, NSData *pqInnerData, NSString *publicKey) {
NSMutableData *dataWithPadding = [[NSMutableData alloc] init];
[dataWithPadding appendData:pqInnerData];
if (dataWithPadding.length > 144) {
return nil;
}
if (dataWithPadding.length != 192) {
int originalLength = (int)dataWithPadding.length;
int numPaddingBytes = 192 - originalLength;
[dataWithPadding setLength:192];
int randomResult = SecRandomCopyBytes(kSecRandomDefault, numPaddingBytes, ((uint8_t *)dataWithPadding.mutableBytes) + originalLength);
if (randomResult != errSecSuccess) {
return nil;
}
}
NSData *dataWithPaddingReversed = reversedBytes(dataWithPadding);
while (true) {
int randomResult = 0;
NSMutableData *tempKey = [[NSMutableData alloc] initWithLength:32];
randomResult = SecRandomCopyBytes(kSecRandomDefault, tempKey.length, tempKey.mutableBytes);
if (randomResult != errSecSuccess) {
return nil;
}
NSMutableData *tempKeyAndDataWithPadding = [[NSMutableData alloc] init];
[tempKeyAndDataWithPadding appendData:tempKey];
[tempKeyAndDataWithPadding appendData:dataWithPadding];
NSMutableData *dataWithHash = [[NSMutableData alloc] init];
[dataWithHash appendData:dataWithPaddingReversed];
[dataWithHash appendData:MTSha256(tempKeyAndDataWithPadding)];
if (dataWithHash.length != 224) {
return nil;
}
NSMutableData *zeroIv = [[NSMutableData alloc] initWithLength:32];
memset(zeroIv.mutableBytes, 0, zeroIv.length);
NSData *aesEncrypted = MTAesEncrypt(dataWithHash, tempKey, zeroIv);
if (aesEncrypted == nil) {
return nil;
}
NSData *shaAesEncrypted = MTSha256(aesEncrypted);
NSMutableData *tempKeyXor = [[NSMutableData alloc] initWithLength:tempKey.length];
if (tempKeyXor.length != shaAesEncrypted.length) {
return nil;
}
for (NSUInteger i = 0; i < tempKey.length; i++) {
((uint8_t *)tempKeyXor.mutableBytes)[i] = ((uint8_t *)tempKey.bytes)[i] ^ ((uint8_t *)shaAesEncrypted.bytes)[i];
}
NSMutableData *keyAesEncrypted = [[NSMutableData alloc] init];
[keyAesEncrypted appendData:tempKeyXor];
[keyAesEncrypted appendData:aesEncrypted];
if (keyAesEncrypted.length != 256) {
return nil;
}
id<MTBignumContext> bignumContext = [encryptionProvider createBignumContext];
if (bignumContext == nil) {
return nil;
}
id<MTRsaPublicKey> rsaPublicKey = [encryptionProvider parseRSAPublicKey:publicKey];
if (rsaPublicKey == nil) {
return nil;
}
id<MTBignum> rsaModule = [bignumContext rsaGetN:rsaPublicKey];
if (rsaModule == nil) {
return nil;
}
id<MTBignum> bignumKeyAesEncrypted = [bignumContext create];
if (bignumKeyAesEncrypted == nil) {
return nil;
}
[bignumContext assignBinTo:bignumKeyAesEncrypted value:keyAesEncrypted];
int compareResult = [bignumContext compare:rsaModule with:bignumKeyAesEncrypted];
if (compareResult <= 0) {
continue;
}
NSData *encryptedData = [encryptionProvider rsaEncryptWithPublicKey:publicKey data:keyAesEncrypted];
NSMutableData *paddedEncryptedData = [[NSMutableData alloc] init];
[paddedEncryptedData appendData:encryptedData];
while (paddedEncryptedData.length < 256) {
uint8_t zero = 0;
[paddedEncryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&zero length:1];
}
if (paddedEncryptedData.length != 256) {
return nil;
}
return paddedEncryptedData;
}
}
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector networkType:(int32_t)networkType
{
if (_stage == MTDatacenterAuthStagePQ && [message.body isKindOfClass:[MTResPqMessage class]])
{
MTResPqMessage *resPqMessage = message.body;
if ([_nonce isEqualToData:resPqMessage.nonce])
{
MTDatacenterAuthPublicKey *publicKey = selectPublicKey(_encryptionProvider, resPqMessage.serverPublicKeyFingerprints, _publicKeys);
if (publicKey == nil && mtProto.cdn && resPqMessage.serverPublicKeyFingerprints.count == 1 && _publicKeys.count == 1) {
publicKey = _publicKeys[0];
}
if (publicKey == nil)
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p couldn't find valid server public key]", self);
}
[self reset:mtProto];
}
else
{
NSData *pqBytes = resPqMessage.pq;
uint64_t pq = 0;
for (int i = 0; i < (int)pqBytes.length; i++)
{
pq <<= 8;
pq |= ((uint8_t *)[pqBytes bytes])[i];
}
uint64_t factP = 0;
uint64_t factQ = 0;
if (!MTFactorize(pq, &factP, &factQ))
{
[self reset:mtProto];
return;
}
_serverNonce = resPqMessage.serverNonce;
NSMutableData *pBytes = [[NSMutableData alloc] init];
uint64_t p = factP;
do
{
[pBytes replaceBytesInRange:NSMakeRange(0, 0) withBytes:&p length:1];
p >>= 8;
} while (p > 0);
_dhP = pBytes;
NSMutableData *qBytes = [[NSMutableData alloc] init];
uint64_t q = factQ;
do
{
[qBytes replaceBytesInRange:NSMakeRange(0, 0) withBytes:&q length:1];
q >>= 8;
} while (q > 0);
_dhQ = qBytes;
_dhPublicKeyFingerprint = [publicKey fingerprintWithEncryptionProvider:_encryptionProvider];
uint8_t nonceBytes[32];
__unused int result = SecRandomCopyBytes(kSecRandomDefault, 32, nonceBytes);
_newNonce = [[NSData alloc] initWithBytes:nonceBytes length:32];
/*
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
*/
if (_tempAuth) {
MTBuffer *innerDataBuffer = [[MTBuffer alloc] init];
[innerDataBuffer appendInt32:(int32_t)0x3c6a84d4];
[innerDataBuffer appendTLBytes:pqBytes];
[innerDataBuffer appendTLBytes:_dhP];
[innerDataBuffer appendTLBytes:_dhQ];
[innerDataBuffer appendBytes:_nonce.bytes length:_nonce.length];
[innerDataBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
[innerDataBuffer appendBytes:_newNonce.bytes length:_newNonce.length];
[innerDataBuffer appendInt32:mtProto.context.tempKeyExpiration];
NSData *innerDataBytes = innerDataBuffer.data;
NSData *encryptedData = nil;
encryptedData = encryptRSAModernPadding(_encryptionProvider, innerDataBytes, publicKey.publicKey);
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p encryptedData length %d]", self, (int)encryptedData.length);
}
_dhEncryptedData = encryptedData;
} else {
MTBuffer *innerDataBuffer = [[MTBuffer alloc] init];
[innerDataBuffer appendInt32:(int32_t)0x83c95aec];
[innerDataBuffer appendTLBytes:pqBytes];
[innerDataBuffer appendTLBytes:_dhP];
[innerDataBuffer appendTLBytes:_dhQ];
[innerDataBuffer appendBytes:_nonce.bytes length:_nonce.length];
[innerDataBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
[innerDataBuffer appendBytes:_newNonce.bytes length:_newNonce.length];
NSData *innerDataBytes = innerDataBuffer.data;
NSData *encryptedData = nil;
encryptedData = encryptRSAModernPadding(_encryptionProvider, innerDataBytes, publicKey.publicKey);
_dhEncryptedData = encryptedData;
}
if (_dhEncryptedData == nil) {
_stage = MTDatacenterAuthStagePQ;
_currentStageMessageId = 0;
_currentStageMessageSeqNo = 0;
_currentStageTransactionId = nil;
[mtProto requestTransportTransaction];
} else {
_stage = MTDatacenterAuthStageReqDH;
_currentStageMessageId = 0;
_currentStageMessageSeqNo = 0;
_currentStageTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
}
}
else if (_stage == MTDatacenterAuthStageReqDH && [message.body isKindOfClass:[MTServerDhParamsMessage class]])
{
MTServerDhParamsMessage *serverDhParamsMessage = message.body;
if ([_nonce isEqualToData:serverDhParamsMessage.nonce] && [_serverNonce isEqualToData:serverDhParamsMessage.serverNonce])
{
if ([serverDhParamsMessage isKindOfClass:[MTServerDhParamsOkMessage class]])
{
NSMutableData *tmpAesKey = [[NSMutableData alloc] init];
NSMutableData *newNonceAndServerNonce = [[NSMutableData alloc] init];
[newNonceAndServerNonce appendData:_newNonce];
[newNonceAndServerNonce appendData:_serverNonce];
NSMutableData *serverNonceAndNewNonce = [[NSMutableData alloc] init];
[serverNonceAndNewNonce appendData:_serverNonce];
[serverNonceAndNewNonce appendData:_newNonce];
[tmpAesKey appendData:MTSha1(newNonceAndServerNonce)];
NSData *serverNonceAndNewNonceHash = MTSha1(serverNonceAndNewNonce);
NSData *serverNonceAndNewNonceHash0_12 = [[NSData alloc] initWithBytes:((uint8_t *)serverNonceAndNewNonceHash.bytes) length:12];
[tmpAesKey appendData:serverNonceAndNewNonceHash0_12];
NSMutableData *tmpAesIv = [[NSMutableData alloc] init];
NSData *serverNonceAndNewNonceHash12_8 = [[NSData alloc] initWithBytes:(((uint8_t *)serverNonceAndNewNonceHash.bytes) + 12) length:8];
[tmpAesIv appendData:serverNonceAndNewNonceHash12_8];
NSMutableData *newNonceAndNewNonce = [[NSMutableData alloc] init];
[newNonceAndNewNonce appendData:_newNonce];
[newNonceAndNewNonce appendData:_newNonce];
[tmpAesIv appendData:MTSha1(newNonceAndNewNonce)];
NSData *newNonce0_4 = [[NSData alloc] initWithBytes:((uint8_t *)_newNonce.bytes) length:4];
[tmpAesIv appendData:newNonce0_4];
NSData *answerWithHash = MTAesDecrypt(((MTServerDhParamsOkMessage *)serverDhParamsMessage).encryptedResponse, tmpAesKey, tmpAesIv);
NSData *answerHash = [[NSData alloc] initWithBytes:((uint8_t *)answerWithHash.bytes) length:20];
NSMutableData *answerData = [[NSMutableData alloc] initWithBytes:(((uint8_t *)answerWithHash.bytes) + 20) length:(answerWithHash.length - 20)];
bool hashVerified = false;
for (int i = 0; i < 16; i++)
{
NSData *computedAnswerHash = MTSha1(answerData);
if ([computedAnswerHash isEqualToData:answerHash])
{
hashVerified = true;
break;
}
[answerData replaceBytesInRange:NSMakeRange(answerData.length - 1, 1) withBytes:NULL length:0];
}
if (!hashVerified)
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p couldn't decode DH params]", self);
}
[self reset:mtProto];
return;
}
MTServerDhInnerDataMessage *dhInnerData = [MTInternalMessageParser parseMessage:answerData];
if (![dhInnerData isKindOfClass:[MTServerDhInnerDataMessage class]])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p couldn't parse decoded DH params]", self);
}
[self reset:mtProto];
return;
}
if (![_nonce isEqualToData:dhInnerData.nonce])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH nonce]", self);
}
[self reset:mtProto];
return;
}
if (![_serverNonce isEqualToData:dhInnerData.serverNonce])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH server nonce]", self);
}
[self reset:mtProto];
return;
}
int32_t innerDataG = dhInnerData.g;
if (innerDataG < 0 || !MTCheckIsSafeG((unsigned int)innerDataG))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g]", self);
}
[self reset:mtProto];
return;
}
NSData *innerDataGA = dhInnerData.gA;
NSData *innerDataDhPrime = dhInnerData.dhPrime;
if (!MTCheckIsSafeGAOrB(_encryptionProvider, innerDataGA, innerDataDhPrime))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g_a]", self);
}
[self reset:mtProto];
return;
}
if (!MTCheckMod(_encryptionProvider, innerDataDhPrime, (unsigned int)innerDataG, mtProto.context.keychain))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g (2)]", self);
}
[self reset:mtProto];
return;
}
if (!MTCheckIsSafePrime(_encryptionProvider, innerDataDhPrime, mtProto.context.keychain))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH prime]", self);
}
[self reset:mtProto];
return;
}
uint8_t bBytes[256];
__unused int result = SecRandomCopyBytes(kSecRandomDefault, 256, bBytes);
NSData *b = [[NSData alloc] initWithBytes:bBytes length:256];
int32_t tmpG = innerDataG;
tmpG = (int32_t)OSSwapInt32(tmpG);
NSData *g = [[NSData alloc] initWithBytes:&tmpG length:4];
NSData *g_b = MTExp(_encryptionProvider, g, b, innerDataDhPrime);
NSData *authKey = MTExp(_encryptionProvider, innerDataGA, b, innerDataDhPrime);
NSData *authKeyHash = MTSha1(authKey);
int64_t authKeyId = 0;
memcpy(&authKeyId, (((uint8_t *)authKeyHash.bytes) + authKeyHash.length - 8), 8);
NSMutableData *serverSaltData = [[NSMutableData alloc] init];
for (int i = 0; i < 8; i++)
{
int8_t a = ((int8_t *)_newNonce.bytes)[i];
int8_t b = ((int8_t *)_serverNonce.bytes)[i];
int8_t x = a ^ b;
[serverSaltData appendBytes:&x length:1];
}
int32_t validUntilTimestamp = ((int32_t)([NSDate date].timeIntervalSince1970)) + mtProto.context.tempKeyExpiration;
_authKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:authKey authKeyId:authKeyId validUntilTimestamp:validUntilTimestamp notBound:_tempAuth];
MTBuffer *clientDhInnerDataBuffer = [[MTBuffer alloc] init];
[clientDhInnerDataBuffer appendInt32:(int32_t)0x6643b654];
[clientDhInnerDataBuffer appendBytes:_nonce.bytes length:_nonce.length];
[clientDhInnerDataBuffer appendBytes:_serverNonce.bytes length:_serverNonce.length];
[clientDhInnerDataBuffer appendInt64:0];
[clientDhInnerDataBuffer appendTLBytes:g_b];
NSData *clientInnerDataBytes = clientDhInnerDataBuffer.data;
NSMutableData *clientDataWithHash = [[NSMutableData alloc] init];
[clientDataWithHash appendData:MTSha1(clientInnerDataBytes)];
[clientDataWithHash appendData:clientInnerDataBytes];
while (clientDataWithHash.length % 16 != 0)
{
uint8_t randomByte = 0;
arc4random_buf(&randomByte, 1);
[clientDataWithHash appendBytes:&randomByte length:1];
}
_encryptedClientData = MTAesEncrypt(clientDataWithHash, tmpAesKey, tmpAesIv);
_stage = MTDatacenterAuthStageKeyVerification;
_currentStageMessageId = 0;
_currentStageMessageSeqNo = 0;
_currentStageTransactionId = nil;
[mtProto requestTransportTransaction];
}
else
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p couldn't set DH params]", self);
}
[self reset:mtProto];
}
}
}
else if (_stage == MTDatacenterAuthStageKeyVerification && [message.body isKindOfClass:[MTSetClientDhParamsResponseMessage class]])
{
MTSetClientDhParamsResponseMessage *setClientDhParamsResponseMessage = message.body;
if ([_nonce isEqualToData:setClientDhParamsResponseMessage.nonce] && [_serverNonce isEqualToData:setClientDhParamsResponseMessage.serverNonce])
{
NSData *authKeyAuxHashFull = MTSha1(_authKey.authKey);
NSData *authKeyAuxHash = [[NSData alloc] initWithBytes:((uint8_t *)authKeyAuxHashFull.bytes) length:8];
NSMutableData *newNonce1 = [[NSMutableData alloc] init];
[newNonce1 appendData:_newNonce];
uint8_t tmp1 = 1;
[newNonce1 appendBytes:&tmp1 length:1];
[newNonce1 appendData:authKeyAuxHash];
NSData *newNonceHash1Full = MTSha1(newNonce1);
NSData *newNonceHash1 = [[NSData alloc] initWithBytes:(((uint8_t *)newNonceHash1Full.bytes) + newNonceHash1Full.length - 16) length:16];
NSMutableData *newNonce2 = [[NSMutableData alloc] init];
[newNonce2 appendData:_newNonce];
uint8_t tmp2 = 2;
[newNonce2 appendBytes:&tmp2 length:1];
[newNonce2 appendData:authKeyAuxHash];
NSData *newNonceHash2Full = MTSha1(newNonce2);
NSData *newNonceHash2 = [[NSData alloc] initWithBytes:(((uint8_t *)newNonceHash2Full.bytes) + newNonceHash2Full.length - 16) length:16];
NSMutableData *newNonce3 = [[NSMutableData alloc] init];
[newNonce3 appendData:_newNonce];
uint8_t tmp3 = 3;
[newNonce3 appendBytes:&tmp3 length:1];
[newNonce3 appendData:authKeyAuxHash];
NSData *newNonceHash3Full = MTSha1(newNonce3);
NSData *newNonceHash3 = [[NSData alloc] initWithBytes:(((uint8_t *)newNonceHash3Full.bytes) + newNonceHash3Full.length - 16) length:16];
if ([setClientDhParamsResponseMessage isKindOfClass:[MTSetClientDhParamsResponseOkMessage class]])
{
if (![newNonceHash1 isEqualToData:((MTSetClientDhParamsResponseOkMessage *)setClientDhParamsResponseMessage).nextNonceHash1])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH answer nonce hash 1]", self);
}
[self reset:mtProto];
}
else
{
_stage = MTDatacenterAuthStageDone;
_currentStageMessageId = 0;
_currentStageMessageSeqNo = 0;
_currentStageTransactionId = nil;
id<MTDatacenterAuthMessageServiceDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(authMessageServiceCompletedWithAuthKey:timestamp:)])
[delegate authMessageServiceCompletedWithAuthKey:_authKey timestamp:message.messageId];
}
}
else if ([setClientDhParamsResponseMessage isKindOfClass:[MTSetClientDhParamsResponseRetryMessage class]])
{
if (![newNonceHash2 isEqualToData:((MTSetClientDhParamsResponseRetryMessage *)setClientDhParamsResponseMessage).nextNonceHash2])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH answer nonce hash 2]", self);
}
[self reset:mtProto];
}
else
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p retry DH]", self);
}
[self reset:mtProto];
}
}
else if ([setClientDhParamsResponseMessage isKindOfClass:[MTSetClientDhParamsResponseFailMessage class]])
{
if (![newNonceHash3 isEqualToData:((MTSetClientDhParamsResponseFailMessage *)setClientDhParamsResponseMessage).nextNonceHash3])
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH answer nonce hash 3]", self);
}
[self reset:mtProto];
}
else
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p server rejected DH params]", self);
}
[self reset:mtProto];
}
}
else
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH params response]", self);
}
[self reset:mtProto];
}
}
}
}
- (void)mtProto:(MTProto *)mtProto protocolErrorReceived:(int32_t)__unused errorCode
{
[self reset:mtProto];
}
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds
{
if (_currentStageTransactionId != nil && [transactionIds containsObject:_currentStageTransactionId])
{
_currentStageTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto
{
if (_currentStageTransactionId != nil)
{
_currentStageTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
@end
@@ -0,0 +1,49 @@
#import <MtProtoKit/MTDatacenterSaltInfo.h>
@implementation MTDatacenterSaltInfo
- (instancetype)initWithSalt:(int64_t)salt firstValidMessageId:(int64_t)firstValidMessageId lastValidMessageId:(int64_t)lastValidMessageId
{
self = [super init];
if (self != nil)
{
_salt = salt;
_firstValidMessageId = firstValidMessageId;
_lastValidMessageId = lastValidMessageId;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_salt = [aDecoder decodeInt64ForKey:@"salt"];
_firstValidMessageId = [aDecoder decodeInt64ForKey:@"firstValidMessageId"];
_lastValidMessageId = [aDecoder decodeInt64ForKey:@"lastValidMessageId"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:_salt forKey:@"salt"];
[aCoder encodeInt64:_firstValidMessageId forKey:@"firstValidMessageId"];
[aCoder encodeInt64:_lastValidMessageId forKey:@"lastValidMessageId"];
}
- (int64_t)validMessageCountAfterId:(int64_t)messageId
{
if (messageId < _firstValidMessageId)
return 0;
return MAX(0, _lastValidMessageId - messageId);
}
- (bool)isValidFutureSaltForMessageId:(int64_t)messageId
{
return _lastValidMessageId > messageId;
}
@end
@@ -0,0 +1,190 @@
#import <MtProtoKit/MTDatacenterTransferAuthAction.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTRequest.h>
#import "MTBuffer.h"
@interface MTDatacenterTransferAuthAction () <MTContextChangeListener>
{
id _authToken;
MTProto *_sourceDatacenterMtProto;
NSInteger _destinationDatacenterId;
MTProto *_destinationDatacenterMtProto;
__weak MTContext *_context;
}
@end
@implementation MTDatacenterTransferAuthAction
- (void)dealloc
{
[self cleanup];
}
- (void)cleanup
{
MTContext *context = _context;
[context removeChangeListener:self];
[_sourceDatacenterMtProto stop];
_sourceDatacenterMtProto = nil;
[_destinationDatacenterMtProto stop];
_destinationDatacenterMtProto = nil;
}
- (void)execute:(MTContext *)context masterDatacenterId:(NSInteger)masterDatacenterId destinationDatacenterId:(NSInteger)destinationDatacenterId authToken:(id)authToken
{
_destinationDatacenterId = destinationDatacenterId;
_context = context;
_authToken = authToken;
if (_destinationDatacenterId != 0 && context != nil && _authToken != nil)
{
if ([_authToken isEqual:[context authTokenForDatacenterWithId:_destinationDatacenterId]])
[self complete];
else
[self beginTransferFromDatacenterId:masterDatacenterId];
}
else
[self fail];
}
- (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken
{
MTContext *currentContext = _context;
if (currentContext == nil || context != currentContext)
return;
if (authToken != nil && [_authToken isEqual:authToken])
{
if (datacenterId == _destinationDatacenterId)
[self complete];
else
[self beginTransferFromDatacenterId:datacenterId];
}
}
- (void)beginTransferFromDatacenterId:(NSInteger)sourceDatacenterId
{
MTContext *context = _context;
if (context == nil)
{
[self fail];
return;
}
_sourceDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:sourceDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
_sourceDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
requestService.forceBackgroundRequests = true;
[_sourceDatacenterMtProto addMessageService:requestService];
[_sourceDatacenterMtProto resume];
MTRequest *request = [[MTRequest alloc] init];
NSData *exportAuthRequestData = nil;
MTExportAuthorizationResponseParser responseParser = [[context.serialization exportAuthorization:(int32_t)_destinationDatacenterId data:&exportAuthRequestData] copy];
[request setPayload:exportAuthRequestData metadata:@"exportAuthorization" shortMetadata:@"exportAuthorization" responseParser:responseParser];
__weak MTDatacenterTransferAuthAction *weakSelf = self;
[request setCompleted:^(MTExportedAuthorizationData *result, __unused MTRequestResponseInfo *info, id error)
{
__strong MTDatacenterTransferAuthAction *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (error == nil)
{
[strongSelf beginTransferWithId:result.authorizationId data:result.authorizationBytes];
}
else
[strongSelf fail];
}];
[requestService addRequest:request];
}
- (void)beginTransferWithId:(int64_t)dataId data:(NSData *)authData
{
[_sourceDatacenterMtProto stop];
_sourceDatacenterMtProto = nil;
MTContext *context = _context;
_destinationDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:_destinationDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
_destinationDatacenterMtProto.canResetAuthData = true;
_destinationDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
requestService.forceBackgroundRequests = true;
[_destinationDatacenterMtProto addMessageService:requestService];
[_destinationDatacenterMtProto resume];
MTRequest *request = [[MTRequest alloc] init];
NSData *importAuthRequestData = [_context.serialization importAuthorization:dataId bytes:authData];
[request setPayload:importAuthRequestData metadata:@"importAuthorization" shortMetadata:@"importAuthorization" responseParser:^id (NSData *data)
{
return @true;
}];
NSInteger destinationDatacenterId = _destinationDatacenterId;
id authToken = _authToken;
__weak MTDatacenterTransferAuthAction *weakSelf = self;
[request setCompleted:^(__unused id result, __unused MTRequestResponseInfo *info, id error)
{
__strong MTDatacenterTransferAuthAction *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (error == nil)
{
[context updateAuthTokenForDatacenterWithId:destinationDatacenterId authToken:authToken];
[strongSelf complete];
}
else
[strongSelf fail];
}];
[requestService addRequest:request];
}
- (void)cancel
{
[self cleanup];
[self fail];
}
- (void)complete
{
id<MTDatacenterTransferAuthActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(datacenterTransferAuthActionCompleted:)])
[delegate datacenterTransferAuthActionCompleted:self];
}
- (void)fail
{
id<MTDatacenterTransferAuthActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(datacenterTransferAuthActionCompleted:)])
[delegate datacenterTransferAuthActionCompleted:self];
}
@end
@@ -0,0 +1,18 @@
#import <MtProtoKit/MTDatacenterVerificationData.h>
@implementation MTDatacenterVerificationData
- (instancetype _Nonnull)initWithDatacenterId:(NSInteger)datacenterId isTestingEnvironment:(bool)isTestingEnvironment {
self = [super init];
if (self != nil) {
_datacenterId = datacenterId;
_isTestingEnvironment = isTestingEnvironment;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"datacenterId: %d, isTestingEnvironment: %d", (int)_datacenterId, _isTestingEnvironment ? 1 : 0];
}
@end
@@ -0,0 +1,29 @@
#import <Foundation/Foundation.h>
@interface MTDestroySessionResponseMessage : NSObject
@end
@interface MTDestroySessionResponseOkMessage : MTDestroySessionResponseMessage
@property (nonatomic, readonly) int64_t sessionId;
- (instancetype)initWithSessionId:(int64_t)sessionId;
@end
@interface MTDestroySessionResponseNoneMessage : MTDestroySessionResponseMessage
@property (nonatomic, readonly) int64_t sessionId;
- (instancetype)initWithSessionId:(int64_t)sessionId;
@end
@interface MTDestroySessionMultipleResponseMessage : MTDestroySessionResponseMessage
@property (nonatomic, strong, readonly) NSData *responsesData;
- (instancetype)initWithResponses:(NSData *)responsesData;
@end
@@ -0,0 +1,47 @@
#import "MTDestroySessionResponseMessage.h"
@implementation MTDestroySessionResponseMessage
@end
@implementation MTDestroySessionResponseOkMessage
- (instancetype)initWithSessionId:(int64_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
@end
@implementation MTDestroySessionResponseNoneMessage
- (instancetype)initWithSessionId:(int64_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
@end
@implementation MTDestroySessionMultipleResponseMessage
- (instancetype)initWithResponses:(NSData *)responsesData
{
self = [super init];
if (self != nil)
{
_responsesData = responsesData;
}
return self;
}
@end
@@ -0,0 +1,20 @@
#import <Foundation/Foundation.h>
@class MTContext;
@class MTDatacenterAddress;
@class MTSignal;
@class MTDatacenterAuthKey;
typedef struct {
uint8_t nonce[16];
} MTPayloadData;
@interface MTDiscoverConnectionSignals : NSObject
+ (NSData * _Nonnull)payloadData:(MTPayloadData * _Nonnull)outPayloadData context:(MTContext * _Nonnull)context address:(MTDatacenterAddress * _Nonnull)address;
+ (MTSignal * _Nonnull)discoverSchemeWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId addressList:(NSArray * _Nonnull)addressList media:(bool)media isProxy:(bool)isProxy;
+ (MTSignal * _Nonnull)checkIfAuthKeyRemovedWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authKey:(MTDatacenterAuthKey * _Nonnull)authKey;
@end
@@ -0,0 +1,267 @@
#import "MTDiscoverConnectionSignals.h"
#import "MTTcpConnection.h"
#import <MtProtoKit/MTTransportScheme.h>
#import <MtProtoKit/MTTcpTransport.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTDatacenterAddress.h>
#import <MtProtoKit/MTDisposable.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTAtomic.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTDatacenterAuthAction.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@implementation MTDiscoverConnectionSignals
+ (NSData *)payloadData:(MTPayloadData *)outPayloadData context:(MTContext *)context address:(MTDatacenterAddress *)address {
uint8_t reqPqBytes[] = {
0, 0, 0, 0, 0, 0, 0, 0, // zero * 8
0, 0, 0, 0, 0, 0, 0, 0, // message id
20, 0, 0, 0, // message length
0xf1, 0x8e, 0x7e, 0xbe, // req_pq_multi
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // nonce
};
MTPayloadData payloadData;
arc4random_buf(&payloadData.nonce, 16);
if (outPayloadData)
*outPayloadData = payloadData;
int64_t messageId = (int64_t)([[NSDate date] timeIntervalSince1970] * 4294967296);
memcpy(reqPqBytes + 8, &messageId, 8);
memcpy(reqPqBytes + 8 + 8 + 4 + 4, payloadData.nonce, 16);
NSMutableData *data = [[NSMutableData alloc] initWithBytes:reqPqBytes length:sizeof(reqPqBytes)];
NSData *secret = address.secret;
if (context.apiEnvironment.socksProxySettings != nil) {
if (context.apiEnvironment.socksProxySettings.secret != nil) {
secret = context.apiEnvironment.socksProxySettings.secret;
}
}
bool extendedPadding = false;
if (secret != nil) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
}
if (extendedPadding) {
uint32_t paddingSize = arc4random_uniform(128);
if (paddingSize != 0) {
uint8_t padding[128];
arc4random_buf(padding, paddingSize);
[data appendBytes:padding length:paddingSize];
}
}
return data;
}
+ (bool)isResponseValid:(NSData *)data payloadData:(MTPayloadData)payloadData {
if (data.length >= 84) {
uint8_t zero[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t resPq[] = { 0x63, 0x24, 0x16, 0x05 };
if (memcmp((uint8_t * const)data.bytes, zero, 8) == 0 && memcmp(((uint8_t * const)data.bytes) + 20, resPq, 4) == 0 && memcmp(((uint8_t * const)data.bytes) + 24, payloadData.nonce, 16) == 0) {
return true;
}
}
return false;
}
+ (bool)isIpv6:(NSString *)ip
{
const char *utf8 = [ip UTF8String];
int success;
struct in6_addr dst6;
success = inet_pton(AF_INET6, utf8, &dst6);
return success == 1;
}
+ (MTSignal *)tcpConnectionWithContext:(MTContext *)context datacenterId:(NSUInteger)datacenterId address:(MTDatacenterAddress *)address;
{
return [[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber)
{
MTPayloadData payloadData;
NSData *data = [self payloadData:&payloadData context:context address:address];
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:context datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:false] interface:nil usageCalculationInfo:nil getLogPrefix:nil];
__weak MTTcpConnection *weakConnection = connection;
connection.connectionOpened = ^
{
__strong MTTcpConnection *strongConnection = weakConnection;
if (strongConnection != nil)
[strongConnection sendDatas:@[data] completion:nil requestQuickAck:false expectDataInResponse:true];
};
MTAtomic *processedData = [[MTAtomic alloc] initWithValue:@false];
connection.connectionReceivedData = ^(NSData *data)
{
[processedData swap:@true];
if ([self isResponseValid:data payloadData:payloadData])
{
if (MTLogEnabled()) {
MTLog(@"success tcp://%@:%d", address.ip, (int)address.port);
}
[subscriber putCompletion];
}
else
{
if (MTLogEnabled()) {
MTLog(@"failed tcp://%@:%d (invalid response)", address.ip, (int)address.port);
}
[subscriber putError:nil];
}
};
connection.connectionClosed = ^
{
__block bool received = false;
[processedData with:^id (NSNumber *value) {
received = [value boolValue];
return nil;
}];
if (!received) {
if (MTLogEnabled()) {
MTLog(@"failed tcp://%@:%d (disconnected)", address.ip, (int)address.port);
}
[subscriber putError:nil];
}
};
if (MTLogEnabled()) {
MTLog(@"trying tcp://%@:%d", address.ip, (int)address.port);
}
[connection start];
return [[MTBlockDisposable alloc] initWithBlock:^
{
[connection stop];
}];
}] startOn:[MTTcpConnection tcpQueue]];
}
+ (MTSignal *)discoverSchemeWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId addressList:(NSArray *)addressList media:(bool)media isProxy:(bool)isProxy
{
NSMutableArray *bestAddressList = [[NSMutableArray alloc] init];
for (MTDatacenterAddress *address in addressList)
{
if (media == address.preferForMedia && isProxy == address.preferForProxy) {
[bestAddressList addObject:address];
}
}
if (bestAddressList.count == 0 && media)
[bestAddressList addObjectsFromArray:addressList];
NSMutableArray *bestTcp4Signals = [[NSMutableArray alloc] init];
NSMutableArray *bestTcp6Signals = [[NSMutableArray alloc] init];
NSMutableArray *bestHttpSignals = [[NSMutableArray alloc] init];
NSMutableDictionary *tcpIpsByPort = [[NSMutableDictionary alloc] init];
for (MTDatacenterAddress *address in bestAddressList) {
NSMutableSet *ips = tcpIpsByPort[@(address.port)];
if (ips == nil) {
ips = [[NSMutableSet alloc] init];
tcpIpsByPort[@(address.port)] = ips;
}
[ips addObject:address.ip];
}
for (MTDatacenterAddress *address in bestAddressList) {
MTTransportScheme *tcpTransportScheme = [[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:address media:media];
if ([self isIpv6:address.ip])
{
MTSignal *signal = [[[[self tcpConnectionWithContext:context datacenterId:datacenterId address:address] then:[MTSignal single:tcpTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]] catch:^MTSignal *(__unused id error)
{
return [MTSignal complete];
}];
[bestTcp6Signals addObject:signal];
}
else
{
MTSignal *tcpConnectionWithTimeout = [[[self tcpConnectionWithContext:context datacenterId:datacenterId address:address] then:[MTSignal single:tcpTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]];
MTSignal *signal = [tcpConnectionWithTimeout catch:^MTSignal *(__unused id error)
{
return [MTSignal complete];
}];
[bestTcp4Signals addObject:signal];
NSArray *alternatePorts = @[@80, @5222];
for (NSNumber *nPort in alternatePorts) {
NSSet *ipsWithPort = tcpIpsByPort[nPort];
if (![ipsWithPort containsObject:address.ip]) {
MTDatacenterAddress *portAddress = [[MTDatacenterAddress alloc] initWithIp:address.ip port:[nPort intValue] preferForMedia:address.preferForMedia restrictToTcp:address.restrictToTcp cdn:address.cdn preferForProxy:address.preferForProxy secret:address.secret];
MTTransportScheme *tcpPortTransportScheme = [[MTTransportScheme alloc] initWithTransportClass:[MTTcpTransport class] address:portAddress media:media];
MTSignal *tcpConnectionWithTimeout = [[[self tcpConnectionWithContext:context datacenterId:datacenterId address:portAddress] then:[MTSignal single:tcpPortTransportScheme]] timeout:5.0 onQueue:[MTQueue concurrentDefaultQueue] orSignal:[MTSignal fail:nil]];
tcpConnectionWithTimeout = [tcpConnectionWithTimeout mapToSignal:^(id next) {
return [[MTSignal single:next] delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]];
}];
MTSignal *signal = [tcpConnectionWithTimeout catch:^MTSignal *(__unused id error) {
return [MTSignal complete];
}];
[bestTcp4Signals addObject:signal];
}
}
}
}
MTSignal *repeatDelaySignal = [[MTSignal complete] delay:1.0 onQueue:[MTQueue concurrentDefaultQueue]];
MTSignal *optimalDelaySignal = [[MTSignal complete] delay:30.0 onQueue:[MTQueue concurrentDefaultQueue]];
MTSignal *firstTcp4Match = [[[[MTSignal mergeSignals:bestTcp4Signals] then:repeatDelaySignal] restart] take:1];
MTSignal *firstTcp6Match = [[[[MTSignal mergeSignals:bestTcp6Signals] then:repeatDelaySignal] restart] take:1];
MTSignal *firstHttpMatch = [[[[MTSignal mergeSignals:bestHttpSignals] then:repeatDelaySignal] restart] take:1];
MTSignal *optimalTcp4Match = [[[[MTSignal mergeSignals:bestTcp4Signals] then:optimalDelaySignal] restart] take:1];
MTSignal *optimalTcp6Match = [[[[MTSignal mergeSignals:bestTcp6Signals] then:optimalDelaySignal] restart] take:1];
MTSignal *anySignal = [[MTSignal mergeSignals:@[firstTcp4Match, firstTcp6Match, firstHttpMatch]] take:1];
MTSignal *optimalSignal = [[MTSignal mergeSignals:@[optimalTcp4Match, optimalTcp6Match]] take:1];
MTSignal *signal = [anySignal mapToSignal:^MTSignal *(MTTransportScheme *scheme)
{
if (![scheme isOptimal])
{
return [[MTSignal single:scheme] then:[optimalSignal delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]]];
}
else
return [MTSignal single:scheme];
}];
return [signal catch:^MTSignal *(id error) {
return [MTSignal complete];
}];
}
+ (MTSignal * _Nonnull)checkIfAuthKeyRemovedWithContext:(MTContext * _Nonnull)context datacenterId:(NSInteger)datacenterId authKey:(MTDatacenterAuthKey *)authKey {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
[[MTContext contextQueue] dispatchOnQueue:^{
MTDatacenterAuthAction *action = [[MTDatacenterAuthAction alloc] initWithAuthKeyInfoSelector:MTDatacenterAuthInfoSelectorEphemeralMain isCdn:false skipBind:false completion:^(__unused MTDatacenterAuthAction *action, bool success) {
[subscriber putNext:@(!success)];
[subscriber putCompletion];
}];
[action execute:context datacenterId:datacenterId];
[disposable setDisposable:[[MTBlockDisposable alloc] initWithBlock:^{
[action cancel];
}]];
}];
return disposable;
}];
}
@end
@@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@class MTContext;
@class MTDiscoverDatacenterAddressAction;
@protocol MTDiscoverDatacenterAddressActionDelegate <NSObject>
- (void)discoverDatacenterAddressActionCompleted:(MTDiscoverDatacenterAddressAction *)action;
@end
@interface MTDiscoverDatacenterAddressAction : NSObject
@property (nonatomic, weak) id<MTDiscoverDatacenterAddressActionDelegate> delegate;
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)cancel;
@end
@@ -0,0 +1,190 @@
#import "MTDiscoverDatacenterAddressAction.h"
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTRequestMessageService.h>
#import <MtProtoKit/MTRequest.h>
@interface MTDiscoverDatacenterAddressAction () <MTContextChangeListener>
{
NSInteger _datacenterId;
__weak MTContext *_context;
NSInteger _targetDatacenterId;
bool _awaitingAddresSetUpdate;
MTProto *_mtProto;
MTRequestMessageService *_requestService;
NSMutableSet *_processedDatacenters;
}
@end
@implementation MTDiscoverDatacenterAddressAction
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_processedDatacenters = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc
{
[self cleanup];
}
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId
{
_datacenterId = datacenterId;
_context = context;
if (_datacenterId != 0 && context != nil)
{
__block bool datacenterAddressIsKnown = false;
__block NSInteger currentDatacenterId = 0;
[context enumerateAddressSetsForDatacenters:^(NSInteger datacenterId, __unused MTDatacenterAddressSet *addressSet, BOOL *stop)
{
if (datacenterId == _datacenterId)
{
datacenterAddressIsKnown = true;
if (stop != NULL)
*stop = true;
}
else if (![_processedDatacenters containsObject:@(datacenterId)])
{
currentDatacenterId = datacenterId;
[_processedDatacenters addObject:@(datacenterId)];
if (stop != NULL)
*stop = true;
}
}];
if (datacenterAddressIsKnown)
[self complete];
else if (currentDatacenterId != 0)
[self askForAnAddressDatacenterWithId:currentDatacenterId useTempAuthKeys:context.useTempAuthKeys];
else
[self fail];
}
else
[self fail];
}
- (void)askForAnAddressDatacenterWithId:(NSInteger)targetDatacenterId useTempAuthKeys:(bool)useTempAuthKeys
{
_targetDatacenterId = targetDatacenterId;
MTContext *context = _context;
if (context == nil)
[self fail];
else
{
if ([context authInfoForDatacenterWithId:_targetDatacenterId selector:MTDatacenterAuthInfoSelectorPersistent] != nil)
{
_mtProto = [[MTProto alloc] initWithContext:context datacenterId:_targetDatacenterId usageCalculationInfo:nil requiredAuthToken:nil authTokenMasterDatacenterId:0];
_mtProto.useTempAuthKeys = useTempAuthKeys;
_requestService = [[MTRequestMessageService alloc] initWithContext:_context];
_requestService.forceBackgroundRequests = true;
[_mtProto addMessageService:_requestService];
[_mtProto resume];
MTRequest *request = [[MTRequest alloc] init];
NSData *getConfigData = nil;
MTRequestDatacenterAddressListParser responseParser = [_context.serialization requestDatacenterAddressWithData:&getConfigData];
[request setPayload:getConfigData metadata:@"getConfig" shortMetadata:@"getConfig" responseParser:responseParser];
__weak MTDiscoverDatacenterAddressAction *weakSelf = self;
[request setCompleted:^(MTDatacenterAddressListData *result, __unused MTRequestResponseInfo *info, id error)
{
__strong MTDiscoverDatacenterAddressAction *strongSelf = weakSelf;
if (strongSelf != nil) {
if (error == nil)
[strongSelf getConfigSuccess:result.addressList[@(strongSelf->_datacenterId)]];
else
[strongSelf getConfigFailed];
}
}];
[_requestService addRequest:request];
}
else {
[context authInfoForDatacenterWithIdRequired:_targetDatacenterId isCdn:false selector:MTDatacenterAuthInfoSelectorPersistent allowUnboundEphemeralKeys:false];
}
}
}
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)__unused authInfo selector:(MTDatacenterAuthInfoSelector)selector
{
if (_context != context || !_awaitingAddresSetUpdate)
return;
if (authInfo == nil) {
return;
}
if (_targetDatacenterId != 0 && _targetDatacenterId == datacenterId)
{
_awaitingAddresSetUpdate = false;
[self askForAnAddressDatacenterWithId:datacenterId useTempAuthKeys:context.useTempAuthKeys];
}
}
- (void)getConfigSuccess:(NSArray *)addressList
{
if (addressList.count != 0)
{
MTContext *context = _context;
[context updateAddressSetForDatacenterWithId:_datacenterId addressSet:[[MTDatacenterAddressSet alloc] initWithAddressList:addressList] forceUpdateSchemes:false];
[self complete];
}
else
[self fail];
}
- (void)getConfigFailed
{
[self cleanup];
[self fail];
}
- (void)cleanup
{
[_mtProto stop];
_mtProto = nil;
}
- (void)cancel
{
[self cleanup];
[self fail];
}
- (void)complete
{
id<MTDiscoverDatacenterAddressActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(discoverDatacenterAddressActionCompleted:)])
[delegate discoverDatacenterAddressActionCompleted:self];
}
- (void)fail
{
id<MTDiscoverDatacenterAddressActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(discoverDatacenterAddressActionCompleted:)])
[delegate discoverDatacenterAddressActionCompleted:self];
}
@end
@@ -0,0 +1,203 @@
#import <MtProtoKit/MTDisposable.h>
#import <pthread/pthread.h>
#import <objc/runtime.h>
@interface MTBlockDisposable () {
void (^_action)();
pthread_mutex_t _lock;
}
@end
@implementation MTBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_action = [block copy];
pthread_mutex_init(&_lock, nil);
}
return self;
}
- (void)dealloc {
void (^freeAction)() = nil;
pthread_mutex_lock(&_lock);
freeAction = _action;
_action = nil;
pthread_mutex_unlock(&_lock);
if (freeAction) {
}
pthread_mutex_destroy(&_lock);
}
- (void)dispose {
void (^disposeAction)() = nil;
pthread_mutex_lock(&_lock);
disposeAction = _action;
_action = nil;
pthread_mutex_unlock(&_lock);
if (disposeAction) {
disposeAction();
}
}
@end
@interface MTMetaDisposable ()
{
pthread_mutex_t _lock;
bool _disposed;
id<MTDisposable> _disposable;
}
@end
@implementation MTMetaDisposable
- (instancetype)init {
self = [super init];
if (self != nil) {
pthread_mutex_init(&_lock, nil);
}
return self;
}
- (void)dealloc {
id<MTDisposable> freeDisposable = nil;
pthread_mutex_lock(&_lock);
if (_disposable) {
freeDisposable = _disposable;
_disposable = nil;
}
pthread_mutex_unlock(&_lock);
if (freeDisposable) {
}
pthread_mutex_destroy(&_lock);
}
- (void)setDisposable:(id<MTDisposable>)disposable {
id<MTDisposable> previousDisposable = nil;
bool disposeImmediately = false;
pthread_mutex_lock(&_lock);
disposeImmediately = _disposed;
if (!disposeImmediately) {
previousDisposable = _disposable;
_disposable = disposable;
}
pthread_mutex_unlock(&_lock);
if (previousDisposable) {
[previousDisposable dispose];
}
if (disposeImmediately) {
[disposable dispose];
}
}
- (void)dispose {
id<MTDisposable> disposable = nil;
pthread_mutex_lock(&_lock);
if (!_disposed) {
_disposed = true;
disposable = _disposable;
_disposable = nil;
}
pthread_mutex_unlock(&_lock);
if (disposable) {
[disposable dispose];
}
}
@end
@interface MTDisposableSet ()
{
pthread_mutex_t _lock;
bool _disposed;
NSMutableArray<id<MTDisposable>> *_disposables;
}
@end
@implementation MTDisposableSet
- (instancetype)init {
self = [super init];
if (self != nil) {
pthread_mutex_init(&_lock, nil);
_disposables = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
NSArray<id<MTDisposable>> *disposables = nil;
pthread_mutex_lock(&_lock);
disposables = _disposables;
_disposables = nil;
pthread_mutex_unlock(&_lock);
if (disposables) {
}
pthread_mutex_destroy(&_lock);
}
- (void)add:(id<MTDisposable>)disposable {
bool disposeImmediately = false;
pthread_mutex_lock(&_lock);
if (_disposed) {
disposeImmediately = true;
} else {
[_disposables addObject:disposable];
}
pthread_mutex_unlock(&_lock);
if (disposeImmediately) {
[disposable dispose];
}
}
- (void)remove:(id<MTDisposable>)disposable {
pthread_mutex_lock(&_lock);
for (NSInteger i = 0; i < _disposables.count; i++) {
if (_disposables[i] == disposable) {
[_disposables removeObjectAtIndex:i];
break;
}
}
pthread_mutex_unlock(&_lock);
}
- (void)dispose {
NSArray<id<MTDisposable>> *disposables = nil;
pthread_mutex_lock(&_lock);
if (!_disposed) {
_disposed = true;
disposables = _disposables;
_disposables = nil;
}
pthread_mutex_unlock(&_lock);
if (disposables) {
for (id<MTDisposable> disposable in disposables) {
[disposable dispose];
}
}
}
@end
@@ -0,0 +1,15 @@
#import <MtProtoKit/MTDropResponseContext.h>
@implementation MTDropResponseContext
- (instancetype)initWithDropMessageId:(int64_t)dropMessageId
{
self = [super init];
if (self != nil)
{
_dropMessageId = dropMessageId;
}
return self;
}
@end
@@ -0,0 +1,24 @@
#import <Foundation/Foundation.h>
@interface MTDropRpcResultMessage : NSObject
@end
@interface MTDropRpcResultUnknownMessage : MTDropRpcResultMessage
@end
@interface MTDropRpcResultDroppedRunningMessage : MTDropRpcResultMessage
@end
@interface MTDropRpcResultDroppedMessage : MTDropRpcResultMessage
@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int32_t seqNo;
@property (nonatomic, readonly) int32_t size;
- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)seqNo size:(int32_t)size;
@end
@@ -0,0 +1,29 @@
#import "MTDropRpcResultMessage.h"
@implementation MTDropRpcResultMessage
@end
@implementation MTDropRpcResultUnknownMessage
@end
@implementation MTDropRpcResultDroppedRunningMessage
@end
@implementation MTDropRpcResultDroppedMessage
- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)seqNo size:(int32_t)size
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_seqNo = seqNo;
_size = size;
}
return self;
}
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
#import <MtProtoKit/MTExportedAuthorizationData.h>
@implementation MTExportedAuthorizationData
- (instancetype)initWithAuthorizationBytes:(NSData *)authorizationBytes authorizationId:(int64_t)authorizationId
{
self = [super init];
if (self != nil)
{
_authorizationBytes = authorizationBytes;
_authorizationId = authorizationId;
}
return self;
}
@end
@@ -0,0 +1 @@
@@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@interface MTFutureSalt : NSObject
@property (nonatomic, readonly) int32_t validSince;
@property (nonatomic, readonly) int32_t validUntil;
@property (nonatomic, readonly) int64_t salt;
- (instancetype)initWithValidSince:(int32_t)validSince validUntil:(int32_t)validUntil salt:(int64_t)salt;
@end
@interface MTFutureSaltsMessage : NSObject
@property (nonatomic, readonly) int64_t requestMessageId;
@property (nonatomic, readonly) int32_t now;
@property (nonatomic, strong, readonly) NSArray *salts;
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId now:(int32_t)now salts:(NSArray *)salts;
@end
@@ -0,0 +1,33 @@
#import "MTFutureSaltsMessage.h"
@implementation MTFutureSalt
- (instancetype)initWithValidSince:(int32_t)validSince validUntil:(int32_t)validUntil salt:(int64_t)salt
{
self = [super init];
if (self != nil)
{
_validSince = validSince;
_validUntil = validUntil;
_salt = salt;
}
return self;
}
@end
@implementation MTFutureSaltsMessage
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId now:(int32_t)now salts:(NSArray *)salts
{
self = [super init];
if (self != nil)
{
_requestMessageId = requestMessageId;
_now = now;
_salts = salts;
}
return self;
}
@end
+97
View File
@@ -0,0 +1,97 @@
#import <MtProtoKit/MTGzip.h>
#import <zlib.h>
@implementation MTGzip
+ (NSData * _Nullable)decompress:(NSData *)data {
const int kMemoryChunkSize = 1024;
NSUInteger length = [data length];
int windowBits = 15 + 32; //Default + gzip header instead of zlib header
int retCode;
#pragma clang diagnostic push
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500
#pragma clang diagnostic ignored "-Wvla-cxx-extension"
#endif
#pragma clang diagnostic ignored "-Wgnu-folding-constant"
unsigned char output[kMemoryChunkSize];
#pragma clang diagnostic pop
uInt gotBack;
NSMutableData *result;
z_stream stream;
if ((length == 0) || (length > UINT_MAX)) //FIXME: Support 64 bit inputs
return nil;
bzero(&stream, sizeof(z_stream));
stream.avail_in = (uInt)length;
stream.next_in = (unsigned char*)[data bytes];
retCode = inflateInit2(&stream, windowBits);
if(retCode != Z_OK)
{
NSLog(@"%s: inflateInit2() failed with error %i", __PRETTY_FUNCTION__, retCode);
return nil;
}
result = [NSMutableData dataWithCapacity:(length * 4)];
do
{
stream.avail_out = kMemoryChunkSize;
stream.next_out = output;
retCode = inflate(&stream, Z_NO_FLUSH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END))
{
NSLog(@"%s: inflate() failed with error %i", __PRETTY_FUNCTION__, retCode);
inflateEnd(&stream);
return nil;
}
gotBack = kMemoryChunkSize - stream.avail_out;
if (gotBack > 0)
[result appendBytes:output length:gotBack];
} while( retCode == Z_OK);
inflateEnd(&stream);
return (retCode == Z_STREAM_END ? result : nil);
}
+ (NSData * _Nullable)compress:(NSData *)data {
if (data.length == 0) {
return data;
}
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = (uint)data.length;
stream.next_in = (Bytef *)(void *)data.bytes;
stream.total_out = 0;
stream.avail_out = 0;
static const NSUInteger ChunkSize = 16384;
NSMutableData *output = nil;
int compression = Z_BEST_COMPRESSION;
if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK)
{
output = [NSMutableData dataWithLength:ChunkSize];
while (stream.avail_out == 0)
{
if (stream.total_out >= output.length)
{
output.length += ChunkSize;
}
stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
stream.avail_out = (uInt)(output.length - stream.total_out);
deflate(&stream, Z_FINISH);
}
deflateEnd(&stream);
output.length = stream.total_out;
}
return output;
}
@end
@@ -0,0 +1,58 @@
#import <MtProtoKit/MTHttpRequestOperation.h>
#import <MtProtoKit/MTDisposable.h>
#import <MtProtoKit/MTSignal.h>
@implementation MTHttpResponse
- (instancetype)initWithHeaders:(NSDictionary *)headers data:(NSData *)data {
self = [super init];
if (self != nil) {
_headers = headers;
_data = data;
}
return self;
}
@end
@implementation MTHttpRequestOperation
+ (MTSignal *)dataForHttpUrl:(NSURL *)url {
return [self dataForHttpUrl:url headers:nil];
}
+ (MTSignal *)dataForHttpUrl:(NSURL *)url headers:(NSDictionary *)headers {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
[request setValue:value forHTTPHeaderField:key];
}];
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse * response, NSError *error) {
if (error) {
[subscriber putError:error];
} else {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
MTHttpResponse *result = [[MTHttpResponse alloc] initWithHeaders:httpResponse.allHeaderFields data:data];
[subscriber putNext:result];
[subscriber putCompletion];
} else {
[subscriber putError:nil];
}
}
}];
[dataTask resume];
__weak NSURLSessionDataTask *weakDataTask = dataTask;
return [[MTBlockDisposable alloc] initWithBlock:^
{
__strong NSURLSessionDataTask *strongDataTask = weakDataTask;
if (strongDataTask) {
[strongDataTask cancel];
}
}];
}];
}
@end
@@ -0,0 +1,22 @@
#import <MtProtoKit/MTIncomingMessage.h>
@implementation MTIncomingMessage
- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)seqNo authKeyId:(int64_t)authKeyId sessionId:(int64_t)sessionId salt:(int64_t)salt timestamp:(NSTimeInterval)timestamp size:(NSInteger)size body:(id)body
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_seqNo = seqNo;
_authKeyId = authKeyId;
_sessionId = sessionId;
_salt = salt;
_timestamp = timestamp;
_size = size;
_body = body;
}
return self;
}
@end
@@ -0,0 +1,428 @@
#import <MtProtoKit/MTInputStream.h>
#import <Foundation/Foundation.h>
#import <MtProtoKit/MTLogging.h>
#if TARGET_OS_IPHONE
# import <endian.h>
#endif
static inline int roundUpInput(int numToRound, int multiple)
{
if (multiple == 0)
{
return numToRound;
}
int remainder = numToRound % multiple;
if (remainder == 0)
{
return numToRound;
}
return numToRound + multiple - remainder;
}
@interface MTInputStream ()
{
NSInputStream *_wrappedInputStream;
}
@end
@implementation MTInputStream
- (instancetype)initWithData:(NSData *)data
{
self = [super init];
if (self != nil)
{
_wrappedInputStream = [[NSInputStream alloc] initWithData:data];
[_wrappedInputStream open];
}
return self;
}
- (void)dealloc
{
[_wrappedInputStream close];
}
- (NSInputStream *)wrappedInputStream
{
return _wrappedInputStream;
}
- (int32_t)readInt32
{
int32_t value = 0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:4] != 4)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read int32");
@throw [[NSException alloc] initWithName:@"MTInputStreamException" reason:@"readInt32 end of stream" userInfo:@{}];
}
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (int32_t)readInt32:(bool *)failed
{
int32_t value = 0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:4] != 4)
{
*failed = true;
return 0;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (int64_t)readInt64
{
int64_t value = 0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:8] != 8)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read int64");
}
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (int64_t)readInt64:(bool *)failed
{
int64_t value = 0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:8] != 8)
{
*failed = true;
return 0;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (double)readDouble
{
double value = 0.0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:8] != 8)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read double");
}
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (double)readDouble:(bool *)failed
{
double value = 0.0;
if ([_wrappedInputStream read:(uint8_t *)&value maxLength:8] != 8)
{
*failed = true;
return 0.0;
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
return value;
}
- (NSData *)readData:(int)length
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read %d bytes", length);
}
}
NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true];
return data;
}
- (NSData *)readData:(int)length failed:(bool *)failed
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
free(bytes);
*failed = true;
return nil;
}
NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true];
return data;
}
- (NSMutableData *)readMutableData:(NSUInteger)length
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read %d bytes", length);
}
}
NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true];
return data;
}
- (NSMutableData *)readMutableData:(NSUInteger)length failed:(bool *)failed
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
free(bytes);
*failed = true;
return nil;
}
NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true];
return data;
}
- (NSString *)readString
{
uint8_t tmp = 0;
[_wrappedInputStream read:&tmp maxLength:1];
int paddingBytes = 0;
int32_t length = tmp;
if (length == 254)
{
length = 0;
[_wrappedInputStream read:((uint8_t *)&length) + 1 maxLength:3];
length >>= 8;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
paddingBytes = roundUpInput(length, 4) - length;
}
else
{
paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
}
NSString *string = nil;
if (length > 0)
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read %d bytes", length);
}
}
string = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true];
}
else
{
string = @"";
}
for (int i = 0; i < paddingBytes; i++)
[_wrappedInputStream read:&tmp maxLength:1];
return string;
}
- (NSString *)readString:(bool *)failed
{
uint8_t tmp = 0;
[_wrappedInputStream read:&tmp maxLength:1];
int paddingBytes = 0;
int32_t length = tmp;
if (length == 254)
{
length = 0;
[_wrappedInputStream read:((uint8_t *)&length) + 1 maxLength:3];
length >>= 8;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
paddingBytes = roundUpInput(length, 4) - length;
}
else
{
paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
}
NSString *string = nil;
if (length > 0)
{
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
free(bytes);
*failed = true;
return nil;
}
string = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true];
}
else
{
string = @"";
}
for (int i = 0; i < paddingBytes; i++)
[_wrappedInputStream read:&tmp maxLength:1];
return string;
}
- (NSData *)readBytes
{
uint8_t tmp = 0;
[_wrappedInputStream read:&tmp maxLength:1];
int paddingBytes = 0;
int32_t length = tmp;
if (length == 254)
{
length = 0;
[_wrappedInputStream read:((uint8_t *)&length) + 1 maxLength:3];
length >>= 8;
paddingBytes = roundUpInput(length, 4) - length;
}
else
{
paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
}
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
if (MTLogEnabled()) {
MTLog(@"***** Couldn't read %d bytes", length);
}
}
NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true];
for (int i = 0; i < paddingBytes; i++)
[_wrappedInputStream read:&tmp maxLength:1];
return result;
}
- (NSData *)readBytes:(bool *)failed
{
uint8_t tmp = 0;
[_wrappedInputStream read:&tmp maxLength:1];
int paddingBytes = 0;
int32_t length = tmp;
if (length == 254)
{
length = 0;
[_wrappedInputStream read:((uint8_t *)&length) + 1 maxLength:3];
length >>= 8;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
paddingBytes = roundUpInput(length, 4) - length;
}
else
{
paddingBytes = roundUpInput(length + 1, 4) - (length + 1);
}
uint8_t *bytes = (uint8_t *)malloc(length);
NSInteger readLen = [_wrappedInputStream read:bytes maxLength:length];
if (readLen != length)
{
free(bytes);
*failed = true;
return nil;
}
NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true];
for (int i = 0; i < paddingBytes; i++)
[_wrappedInputStream read:&tmp maxLength:1];
return result;
}
@end
@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface MTInternalMessageParser : NSObject
+ (id)parseMessage:(NSData *)data;
+ (id)unwrapMessage:(NSData *)data;
@end
@@ -0,0 +1,758 @@
#import "MTInternalMessageParser.h"
#import "MTBufferReader.h"
#import "MTResPqMessage.h"
#import "MTRpcResultMessage.h"
#import <MtProtoKit/MTRpcError.h>
#import "MTDropRpcResultMessage.h"
#import "MTServerDhParamsMessage.h"
#import "MTServerDhInnerDataMessage.h"
#import "MTSetClientDhParamsResponseMessage.h"
#import "MTMsgsAckMessage.h"
#import "MTMsgsStateReqMessage.h"
#import "MTMsgsStateInfoMessage.h"
#import "MTMsgDetailedInfoMessage.h"
#import "MTMsgAllInfoMessage.h"
#import "MTMessage.h"
#import "MTMsgResendReqMessage.h"
#import "MTBadMsgNotificationMessage.h"
#import "MTPingMessage.h"
#import "MTPongMessage.h"
#import "MTNewSessionCreatedMessage.h"
#import "MTDestroySessionResponseMessage.h"
#import "MTMsgContainerMessage.h"
#import "MTFutureSaltsMessage.h"
#import <MtProtoKit/MTLogging.h>
#import <zlib.h>
@implementation MTInternalMessageParser
+ (id)parseMessage:(NSData *)data
{
MTBufferReader *reader = [[MTBufferReader alloc] initWithData:data];
int32_t signature = 0;
if ([reader readInt32:&signature])
{
switch (signature)
{
case (int32_t)0x05162463:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSData *pq = nil;
if (![reader readTLBytes:&pq])
return nil;
if (![reader readInt32:NULL])
return nil;
int32_t count = 0;
if (![reader readInt32:&count])
return nil;
NSMutableArray *serverPublicKeyFingerprints = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t fingerprint = 0;
if (![reader readInt64:&fingerprint])
return nil;
[serverPublicKeyFingerprints addObject:@(fingerprint)];
}
return [[MTResPqMessage alloc] initWithNonce:nonce serverNonce:serverNonce pq:pq serverPublicKeyFingerprints:serverPublicKeyFingerprints];
}
case (int32_t)0x79cb045d:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSMutableData *nextNonceHash = [[NSMutableData alloc] init];
[nextNonceHash setLength:16];
if (![reader readBytes:nextNonceHash.mutableBytes length:16])
return nil;
return [[MTServerDhParamsFailMessage alloc] initWithNonce:nonce serverNonce:serverNonce nextNonceHash:nextNonceHash];
}
case (int32_t)0xd0e8075c:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSData *encryptedResponse = nil;
if (![reader readTLBytes:&encryptedResponse])
return nil;
return [[MTServerDhParamsOkMessage alloc] initWithNonce:nonce serverNonce:serverNonce encryptedResponse:encryptedResponse];
}
case (int32_t)0xb5890dba:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
int32_t g = 0;
if (![reader readInt32:&g])
return nil;
NSData *dhPrime = nil;
if (![reader readTLBytes:&dhPrime])
return nil;
NSData *gA = nil;
if (![reader readTLBytes:&gA])
return nil;
int32_t serverTime = 0;
if (![reader readInt32:&serverTime])
return nil;
return [[MTServerDhInnerDataMessage alloc] initWithNonce:nonce serverNonce:serverNonce g:g dhPrime:dhPrime gA:gA serverTime:serverTime];
}
case (int32_t)0x3bcbf734:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSMutableData *nextNonceHash1 = [[NSMutableData alloc] init];
[nextNonceHash1 setLength:16];
if (![reader readBytes:nextNonceHash1.mutableBytes length:16])
return nil;
return [[MTSetClientDhParamsResponseOkMessage alloc] initWithNonce:nonce serverNonce:serverNonce nextNonceHash1:nextNonceHash1];
}
case (int32_t)0x46dc1fb9:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSMutableData *nextNonceHash2 = [[NSMutableData alloc] init];
[nextNonceHash2 setLength:16];
if (![reader readBytes:nextNonceHash2.mutableBytes length:16])
return nil;
return [[MTSetClientDhParamsResponseRetryMessage alloc] initWithNonce:nonce serverNonce:serverNonce nextNonceHash2:nextNonceHash2];
}
case (int32_t)0xa69dae02:
{
NSMutableData *nonce = [[NSMutableData alloc] init];
[nonce setLength:16];
if (![reader readBytes:nonce.mutableBytes length:16])
return nil;
NSMutableData *serverNonce = [[NSMutableData alloc] init];
[serverNonce setLength:16];
if (![reader readBytes:serverNonce.mutableBytes length:16])
return nil;
NSMutableData *nextNonceHash3 = [[NSMutableData alloc] init];
[nextNonceHash3 setLength:16];
if (![reader readBytes:nextNonceHash3.mutableBytes length:16])
return nil;
return [[MTSetClientDhParamsResponseFailMessage alloc] initWithNonce:nonce serverNonce:serverNonce nextNonceHash3:nextNonceHash3];
}
case (int32_t)0xf35c6d01:
{
int64_t requestMessageId = 0;
if (![reader readInt64:&requestMessageId])
return nil;
NSData *responseData = [reader readRest];
return [[MTRpcResultMessage alloc] initWithRequestMessageId:requestMessageId data:responseData];
}
case (int32_t)0x2144ca19:
{
int32_t errorCode = 0;
if (![reader readInt32:&errorCode])
return nil;
NSString *errorDescription = @"";
if (![reader readTLString:&errorDescription])
return nil;
return [[MTRpcError alloc] initWithErrorCode:errorCode errorDescription:errorDescription];
}
case (int32_t)0x5e2ad36e:
{
return [[MTDropRpcResultUnknownMessage alloc] init];
}
case (int32_t)0xcd78e586:
{
return [[MTDropRpcResultDroppedRunningMessage alloc] init];
}
case (int32_t)0xa43ad8b7:
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
return nil;
int32_t seqNo = 0;
if (![reader readInt32:&seqNo])
return nil;
int32_t size = 0;
if (![reader readInt32:&size])
return nil;
return [[MTDropRpcResultDroppedMessage alloc] initWithMessageId:messageId seqNo:seqNo size:size];
}
case (int32_t)0xda69fb52:
{
if (![reader readInt32:NULL])
return nil;
int32_t count = 0;
if (![reader readInt32:&count])
return nil;
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
return nil;
[messageIds addObject:@(messageId)];
}
return [[MTMsgsStateReqMessage alloc] initWithMessageIds:messageIds];
}
case (int32_t)0x04deb57d:
{
int64_t requestMessageId = 0;
if (![reader readInt64:&requestMessageId])
return nil;
NSData *info = nil;
if (![reader readTLBytes:&info])
return nil;
return [[MTMsgsStateInfoMessage alloc] initWithRequestMessageId:requestMessageId info:info];
}
case (int32_t)0x276d3ec6:
{
int64_t requestMessageId = 0;
if (![reader readInt64:&requestMessageId])
return nil;
int64_t responseMessageId = 0;
if (![reader readInt64:&responseMessageId])
return nil;
int32_t responseLength = 0;
if (![reader readInt32:&responseLength])
return nil;
int32_t status = 0;
if (![reader readInt32:&status])
return nil;
return [[MTMsgDetailedResponseInfoMessage alloc] initWithRequestMessageId:requestMessageId responseMessageId:responseMessageId responseLength:responseLength status:status];
}
case (int32_t)0x809db6df:
{
int64_t responseMessageId = 0;
if (![reader readInt64:&responseMessageId])
return nil;
int32_t responseLength = 0;
if (![reader readInt32:&responseLength])
return nil;
int32_t status = 0;
if (![reader readInt32:&status])
return nil;
return [[MTMsgDetailedInfoMessage alloc] initWithResponseMessageId:responseMessageId responseLength:responseLength status:status];
}
case (int32_t)0x8cc0d131:
{
if (![reader readInt32:NULL])
return nil;
int32_t count = 0;
if (![reader readInt32:&count])
return nil;
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
return nil;
[messageIds addObject:@(messageId)];
}
NSData *info = nil;
if (![reader readTLBytes:&info])
return nil;
return [[MTMsgAllInfoMessage alloc] initWithMessageIds:messageIds info:info];
}
case (int32_t)0xe06046b2:
{
int32_t messageSignature = 0;
if (![reader readInt32:&messageSignature] || messageSignature != (int32_t)0x5bb8e511)
return nil;
int64_t messageId = 0;
if (![reader readInt64:&messageId])
return nil;
int32_t seqNo = 0;
if (![reader readInt32:&seqNo])
return nil;
int32_t length = 0;
if (![reader readInt32:&length])
return nil;
NSData *data = [reader readRest];
if (data.length != (NSUInteger)length)
return nil;
return [[MTMessage alloc] initWithMessageId:messageId seqNo:seqNo data:data];
}
case (int32_t)0x7d861a08:
{
int32_t count = 0;
if (![reader readInt32:&count])
return nil;
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
return nil;
[messageIds addObject:@(messageId)];
}
return [[MTMsgResendReqMessage alloc] initWithMessageIds:messageIds];
}
case (int32_t)0xa7eff811:
{
int64_t badMessageId = 0;
if (![reader readInt64:&badMessageId])
return nil;
int32_t badMessageSeqNo = 0;
if (![reader readInt32:&badMessageSeqNo])
return nil;
int32_t errorCode = 0;
if (![reader readInt32:&errorCode])
return nil;
return [[MTBadMsgNotificationMessage alloc] initWithBadMessageId:badMessageId badMessageSeqNo:badMessageSeqNo errorCode:errorCode];
}
case (int32_t)0xedab447b:
{
int64_t badMessageId = 0;
if (![reader readInt64:&badMessageId])
return nil;
int32_t badMessageSeqNo = 0;
if (![reader readInt32:&badMessageSeqNo])
return nil;
int32_t errorCode = 0;
if (![reader readInt32:&errorCode])
return nil;
int64_t nextServerSalt = 0;
if (![reader readInt64:&nextServerSalt])
return nil;
return [[MTBadServerSaltNotificationMessage alloc] initWithBadMessageId:badMessageId badMessageSeqNo:badMessageSeqNo errorCode:errorCode nextServerSalt:nextServerSalt];
}
case (int32_t)0x62d6b459:
{
int32_t vectorSignature = 0;
if (![reader readInt32:&vectorSignature])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msgs_ack can't read vectorSignature]");
}
return nil;
}
else if (vectorSignature != (int32_t)0x1cb5c415)
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msgs_ack invalid vectorSignature]");
}
return nil;
}
int32_t count = 0;
if (![reader readInt32:&count])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msgs_ack can't read count]");
}
return nil;
}
NSMutableArray *messageIds = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msgs_ack can't read messageId]");
}
return nil;
}
[messageIds addObject:@(messageId)];
}
return [[MTMsgsAckMessage alloc] initWithMessageIds:messageIds];
}
case (int32_t)0x7abe77ec:
{
int64_t pingId = 0;
if (![reader readInt64:&pingId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: ping can't read pingId]");
}
return nil;
}
return [[MTPingMessage alloc] initWithPingId:pingId];
}
case (int32_t)0x347773c5:
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: pong can't read messageId]");
}
return nil;
}
int64_t pingId = 0;
if (![reader readInt64:&pingId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: pong can't read pingId]");
}
return nil;
}
return [[MTPongMessage alloc] initWithMessageId:messageId pingId:pingId];
}
case (int32_t)0x9ec20908:
{
int64_t firstMessageId = 0;
if (![reader readInt64:&firstMessageId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: new_session_created can't read firstMessageId]");
}
return nil;
}
int64_t uniqueId = 0;
if (![reader readInt64:&uniqueId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: new_session_created can't read uniqueId]");
}
return nil;
}
int64_t serverSalt = 0;
if (![reader readInt64:&serverSalt])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: new_session_created can't read serverSalt]");
}
return nil;
}
return [[MTNewSessionCreatedMessage alloc] initWithFirstMessageId:firstMessageId uniqueId:uniqueId serverSalt:serverSalt];
}
case (int32_t)0xe22045fc:
{
int64_t sessionId = 0;
if (![reader readInt64:&sessionId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: destroy_session_ok can't read sessionId]");
}
return nil;
}
return [[MTDestroySessionResponseOkMessage alloc] initWithSessionId:sessionId];
}
case (int32_t)0x62d350c9:
{
int64_t sessionId = 0;
if (![reader readInt64:&sessionId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: destroy_session_none can't read sessionId]");
}
return nil;
}
return [[MTDestroySessionResponseNoneMessage alloc] initWithSessionId:sessionId];
}
case (int32_t)0xfb95abcd:
{
NSData *responsesData = [reader readRest];
return [[MTDestroySessionMultipleResponseMessage alloc] initWithResponses:responsesData];
}
case (int32_t)0x73f1f8dc:
{
int32_t count = 0;
if (![reader readInt32:&count])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container can't read count]");
}
return nil;
}
NSMutableArray *messages = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int64_t messageId = 0;
if (![reader readInt64:&messageId])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container can't read messageId]");
}
return nil;
}
int32_t seqNo = 0;
if (![reader readInt32:&seqNo])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container can't read seqNo]");
}
return nil;
}
int32_t length = 0;
if (![reader readInt32:&length])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container can't read length]");
}
return nil;
}
if (length < 0 || length > 16 * 1024 * 1024)
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container invalid length %d]", length);
}
return nil;
}
NSMutableData *messageData = [[NSMutableData alloc] init];
[messageData setLength:(NSUInteger)length];
if (![reader readBytes:messageData.mutableBytes length:(NSUInteger)length])
{
if (MTLogEnabled()) {
MTLog(@"[MTInternalMessageParser: msg_container can't read bytes]");
}
return nil;
}
[messages addObject:[[MTMessage alloc] initWithMessageId:messageId seqNo:seqNo data:messageData]];
}
return [[MTMsgContainerMessage alloc] initWithMessages:messages];
}
case (int32_t)0xae500895:
{
int64_t requestMessageId = 0;
if (![reader readInt64:&requestMessageId])
return nil;
int32_t now = 0;
if (![reader readInt32:&now])
return nil;
int32_t count = 0;
if (![reader readInt32:&count])
return nil;
NSMutableArray *salts = [[NSMutableArray alloc] init];
for (int32_t i = 0; i < count; i++)
{
int32_t validSince = 0;
if (![reader readInt32:&validSince])
return nil;
int32_t validUntil = 0;
if (![reader readInt32:&validUntil])
return nil;
int64_t salt = 0;
if (![reader readInt64:&salt])
return nil;
[salts addObject:[[MTFutureSalt alloc] initWithValidSince:validSince validUntil:validUntil salt:salt]];
}
return [[MTFutureSaltsMessage alloc] initWithRequestMessageId:requestMessageId now:now salts:salts];
}
default:
break;
}
}
return nil;
}
+ (NSData *)readBytes:(NSData *)data skippingLength:(NSUInteger)skipLength
{
NSUInteger offset = skipLength;
uint8_t tmp = 0;
[data getBytes:&tmp range:NSMakeRange(offset, 1)];
offset += 1;
int32_t length = tmp;
if (length == 254)
{
length = 0;
[data getBytes:((uint8_t *)&length) + 1 range:NSMakeRange(offset, 3)];
offset += 3;
length >>= 8;
}
return [data subdataWithRange:NSMakeRange(offset, length)];
}
+ (NSData *)decompressGZip:(NSData *)data
{
const int kMemoryChunkSize = 1024;
NSUInteger length = [data length];
int windowBits = 15 + 32; //Default + gzip header instead of zlib header
int retCode;
#pragma clang diagnostic push
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 180500
#pragma clang diagnostic ignored "-Wvla-cxx-extension"
#endif
#pragma clang diagnostic ignored "-Wgnu-folding-constant"
unsigned char output[kMemoryChunkSize];
#pragma clang diagnostic pop
uInt gotBack;
NSMutableData *result;
z_stream stream;
if ((length == 0) || (length > UINT_MAX)) //FIXME: Support 64 bit inputs
return nil;
bzero(&stream, sizeof(z_stream));
stream.avail_in = (uInt)length;
stream.next_in = (unsigned char*)[data bytes];
retCode = inflateInit2(&stream, windowBits);
if(retCode != Z_OK)
{
NSLog(@"%s: inflateInit2() failed with error %i", __PRETTY_FUNCTION__, retCode);
return nil;
}
result = [NSMutableData dataWithCapacity:(length * 4)];
do
{
stream.avail_out = kMemoryChunkSize;
stream.next_out = output;
retCode = inflate(&stream, Z_NO_FLUSH);
if ((retCode != Z_OK) && (retCode != Z_STREAM_END))
{
NSLog(@"%s: inflate() failed with error %i", __PRETTY_FUNCTION__, retCode);
inflateEnd(&stream);
return nil;
}
gotBack = kMemoryChunkSize - stream.avail_out;
if (gotBack > 0)
[result appendBytes:output length:gotBack];
} while( retCode == Z_OK);
inflateEnd(&stream);
return (retCode == Z_STREAM_END ? result : nil);
}
+ (NSData *)unwrapMessage:(NSData *)data
{
if (data.length < 4)
return data;
int32_t signature = 0;
[data getBytes:&signature length:4];
if (signature == (int32_t)0x3072cfa1)
{
NSData *packedData = [self readBytes:data skippingLength:4];
if (packedData != nil)
{
NSData *unpackedData = [self decompressGZip:packedData];
return unpackedData;
}
}
return data;
}
@end
@@ -0,0 +1,18 @@
#import <MtProtoKit/MTKeychain.h>
@implementation MTDeprecated
+ (id)unarchiveDeprecatedWithData:(NSData *)data {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@try {
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
} @catch(NSException *e) {
return nil;
}
#pragma clang diagnostic pop
}
@end
+57
View File
@@ -0,0 +1,57 @@
#import <MtProtoKit/MTLogging.h>
static void (*loggingFunction)(NSString *) = NULL;
static void (*shortLoggingFunction)(NSString *) = NULL;
static bool MTLogEnabledValue = true;
bool MTLogEnabled() {
return loggingFunction != NULL && MTLogEnabledValue;
}
void MTLog(NSString *format, ...) {
va_list L;
va_start(L, format);
if (loggingFunction != NULL) {
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
loggingFunction(string);
}
va_end(L);
}
void MTLogWithPrefix(NSString *(^getLogPrefix)(), NSString *format, ...) {
va_list L;
va_start(L, format);
if (loggingFunction != NULL) {
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
if (getLogPrefix) {
NSString *prefix = getLogPrefix();
if (prefix) {
string = [prefix stringByAppendingString:string];
}
}
loggingFunction(string);
}
va_end(L);
}
void MTShortLog(NSString *format, ...) {
va_list L;
va_start(L, format);
if (shortLoggingFunction != NULL) {
NSString *string = [[NSString alloc] initWithFormat:format arguments:L];
shortLoggingFunction(string);
}
va_end(L);
}
void MTLogSetLoggingFunction(void (*function)(NSString *)) {
loggingFunction = function;
}
void MTLogSetShortLoggingFunction(void (*function)(NSString *)) {
shortLoggingFunction = function;
}
void MTLogSetEnabled(bool enabled) {
MTLogEnabledValue = enabled;
}
+11
View File
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface MTMessage : NSObject
@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int32_t seqNo;
@property (nonatomic, strong, readonly) NSData *data;
- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)seqNo data:(NSData *)data;
@end
+17
View File
@@ -0,0 +1,17 @@
#import "MTMessage.h"
@implementation MTMessage
- (instancetype)initWithMessageId:(int64_t)messageId seqNo:(int32_t)seqNo data:(NSData *)data
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_seqNo = seqNo;
_data = data;
}
return self;
}
@end
@@ -0,0 +1,121 @@
#import <MtProtoKit/MTMessageEncryptionKey.h>
#import <MtProtoKit/MTEncryption.h>
@implementation MTMessageEncryptionKey
+ (instancetype)messageEncryptionKeyForAuthKey:(NSData *)authKey messageKey:(NSData *)messageKey toClient:(bool)toClient
{
#ifdef DEBUG
NSAssert(authKey != nil, @"authKey should not be nil");
NSAssert(messageKey != nil, @"message key should not be nil");
#endif
if (authKey == nil || authKey.length == 0 || messageKey == nil || messageKey.length == 0)
return nil;
int x = toClient ? 8 : 0;
NSData *sha1_a = nil;
{
NSMutableData *data = [[NSMutableData alloc] init];
[data appendData:messageKey];
[data appendBytes:(((int8_t *)authKey.bytes) + x) length:32];
sha1_a = MTSha1(data);
}
NSData *sha1_b = nil;
{
NSMutableData *data = [[NSMutableData alloc] init];
[data appendBytes:(((int8_t *)authKey.bytes) + 32 + x) length:16];
[data appendData:messageKey];
[data appendBytes:(((int8_t *)authKey.bytes) + 48 + x) length:16];
sha1_b = MTSha1(data);
}
NSData *sha1_c = nil;
{
NSMutableData *data = [[NSMutableData alloc] init];
[data appendBytes:(((int8_t *)authKey.bytes) + 64 + x) length:32];
[data appendData:messageKey];
sha1_c = MTSha1(data);
}
NSData *sha1_d = nil;
{
NSMutableData *data = [[NSMutableData alloc] init];
[data appendData:messageKey];
[data appendBytes:(((int8_t *)authKey.bytes) + 96 + x) length:32];
sha1_d = MTSha1(data);
}
NSMutableData *aesKey = [[NSMutableData alloc] init];
[aesKey appendBytes:(((int8_t *)sha1_a.bytes)) length:8];
[aesKey appendBytes:(((int8_t *)sha1_b.bytes) + 8) length:12];
[aesKey appendBytes:(((int8_t *)sha1_c.bytes) + 4) length:12];
NSMutableData *aesIv = [[NSMutableData alloc] init];
[aesIv appendBytes:(((int8_t *)sha1_a.bytes) + 8) length:12];
[aesIv appendBytes:(((int8_t *)sha1_b.bytes)) length:8];
[aesIv appendBytes:(((int8_t *)sha1_c.bytes) + 16) length:4];
[aesIv appendBytes:(((int8_t *)sha1_d.bytes)) length:8];
MTMessageEncryptionKey *result = [[MTMessageEncryptionKey alloc] init];
result->_key = [[NSData alloc] initWithData:aesKey];
result->_iv = [[NSData alloc] initWithData:aesIv];
return result;
}
+ (instancetype)messageEncryptionKeyV2ForAuthKey:(NSData *)authKey messageKey:(NSData *)messageKey toClient:(bool)toClient {
#ifdef DEBUG
NSAssert(authKey != nil, @"authKey should not be nil");
NSAssert(messageKey != nil, @"message key should not be nil");
#endif
if (authKey == nil || authKey.length == 0 || messageKey == nil || messageKey.length == 0)
return nil;
int xValue = toClient ? 8 : 0;
NSMutableData *sha256_a_data = [[NSMutableData alloc] init];
[sha256_a_data appendData:messageKey];
[sha256_a_data appendBytes:authKey.bytes + xValue length:36];
NSData *sha256_a = MTSha256(sha256_a_data);
NSMutableData *sha256_b_data = [[NSMutableData alloc] init];
[sha256_b_data appendBytes:authKey.bytes + 40 + xValue length:36];
[sha256_b_data appendData:messageKey];
NSData *sha256_b = MTSha256(sha256_b_data);
NSMutableData *aesKey = [[NSMutableData alloc] init];
[aesKey appendBytes:sha256_a.bytes + 0 length:8];
[aesKey appendBytes:sha256_b.bytes + 8 length:16];
[aesKey appendBytes:sha256_a.bytes + 24 length:8];
NSMutableData *aesIv = [[NSMutableData alloc] init];
[aesIv appendBytes:sha256_b.bytes + 0 length:8];
[aesIv appendBytes:sha256_a.bytes + 8 length:16];
[aesIv appendBytes:sha256_b.bytes + 24 length:8];
MTMessageEncryptionKey *result = [[MTMessageEncryptionKey alloc] init];
result->_key = [[NSData alloc] initWithData:aesKey];
result->_iv = [[NSData alloc] initWithData:aesIv];
return result;
}
- (instancetype)initWithKey:(NSData *)key iv:(NSData *)iv
{
self = [super init];
if (self != nil)
{
_key = key;
_iv = iv;
}
return self;
}
@end
@@ -0,0 +1,29 @@
#import <MtProtoKit/MTMessageTransaction.h>
#import <MtProtoKit/MTInternalId.h>
#import <libkern/OSAtomic.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
MTInternalIdClass(MTMessageTransaction)
#pragma clang diagnostic pop
@implementation MTMessageTransaction
- (instancetype)initWithMessagePayload:(NSArray *)messagePayload prepared:(void (^)(NSDictionary *messageInternalIdToPreparedMessage))prepared failed:(void (^)())failed completion:(void (^)(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, NSDictionary *messageInternalIdToQuickAckId))completion
{
self = [super init];
if (self != nil)
{
_internalId = [[MTInternalId(MTMessageTransaction) alloc] init];
_messagePayload = messagePayload;
_completion = [completion copy];
_prepared = [prepared copy];
_failed = [failed copy];
}
return self;
}
@end
@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface MTMsgAllInfoMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messageIds;
@property (nonatomic, strong, readonly) NSData *info;
- (instancetype)initWithMessageIds:(NSArray *)messageIds info:(NSData *)info;
@end
@@ -0,0 +1,16 @@
#import "MTMsgAllInfoMessage.h"
@implementation MTMsgAllInfoMessage
- (instancetype)initWithMessageIds:(NSArray *)messageIds info:(NSData *)info
{
self = [super init];
if (self != nil)
{
_messageIds = messageIds;
_info = info;
}
return self;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgContainerMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messages;
- (instancetype)initWithMessages:(NSArray *)messages;
@end
@@ -0,0 +1,15 @@
#import "MTMsgContainerMessage.h"
@implementation MTMsgContainerMessage
- (instancetype)initWithMessages:(NSArray *)messages
{
self = [super init];
if (self != nil)
{
_messages = messages;
}
return self;
}
@end
@@ -0,0 +1,19 @@
#import <Foundation/Foundation.h>
@interface MTMsgDetailedInfoMessage : NSObject
@property (nonatomic, readonly) int64_t responseMessageId;
@property (nonatomic, readonly) int32_t responseLength;
@property (nonatomic, readonly) int32_t status;
- (instancetype)initWithResponseMessageId:(int64_t)responseMessageId responseLength:(int32_t)responseLength status:(int32_t)status;
@end
@interface MTMsgDetailedResponseInfoMessage : MTMsgDetailedInfoMessage
@property (nonatomic, readonly) int64_t requestMessageId;
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId responseMessageId:(int64_t)responseMessageId responseLength:(int32_t)responseLength status:(int32_t)status;
@end
@@ -0,0 +1,31 @@
#import "MTMsgDetailedInfoMessage.h"
@implementation MTMsgDetailedInfoMessage
- (instancetype)initWithResponseMessageId:(int64_t)responseMessageId responseLength:(int32_t)responseLength status:(int32_t)status
{
self = [super init];
if (self != nil)
{
_responseMessageId = responseMessageId;
_responseLength = responseLength;
_status = status;
}
return self;
}
@end
@implementation MTMsgDetailedResponseInfoMessage
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId responseMessageId:(int64_t)responseMessageId responseLength:(int32_t)responseLength status:(int32_t)status
{
self = [super initWithResponseMessageId:responseMessageId responseLength:responseLength status:status];
if (self != nil)
{
_requestMessageId = requestMessageId;
}
return self;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgResendReqMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messageIds;
- (instancetype)initWithMessageIds:(NSArray *)messageIds;
@end
@@ -0,0 +1,15 @@
#import "MTMsgResendReqMessage.h"
@implementation MTMsgResendReqMessage
- (instancetype)initWithMessageIds:(NSArray *)messageIds
{
self = [super init];
if (self != nil)
{
_messageIds = messageIds;
}
return self;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgsAckMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messageIds;
- (instancetype)initWithMessageIds:(NSArray *)messageIds;
@end
@@ -0,0 +1,15 @@
#import "MTMsgsAckMessage.h"
@implementation MTMsgsAckMessage
- (instancetype)initWithMessageIds:(NSArray *)messageIds
{
self = [super init];
if (self != nil)
{
_messageIds = messageIds;
}
return self;
}
@end
@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface MTMsgsStateInfoMessage : NSObject
@property (nonatomic, readonly) int64_t requestMessageId;
@property (nonatomic, strong, readonly) NSData *info;
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId info:(NSData *)info;
@end
@@ -0,0 +1,16 @@
#import "MTMsgsStateInfoMessage.h"
@implementation MTMsgsStateInfoMessage
- (instancetype)initWithRequestMessageId:(int64_t)requestMessageId info:(NSData *)info
{
self = [super init];
if (self != nil)
{
_requestMessageId = requestMessageId;
_info = info;
}
return self;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgsStateReqMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messageIds;
- (instancetype)initWithMessageIds:(NSArray *)messageIds;
@end
@@ -0,0 +1,15 @@
#import "MTMsgsStateReqMessage.h"
@implementation MTMsgsStateReqMessage
- (instancetype)initWithMessageIds:(NSArray *)messageIds
{
self = [super init];
if (self != nil)
{
_messageIds = messageIds;
}
return self;
}
@end
@@ -0,0 +1,175 @@
#import <MtProtoKit/MTNetworkAvailability.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTTimer.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <objc/message.h>
static void MTAvailabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);
@class MTNetworkAvailability;
@interface MTNetworkAvailabilityContext : NSObject
@property (nonatomic, weak) MTNetworkAvailability *context;
@end
@implementation MTNetworkAvailabilityContext
@end
static const void *MTNetworkAvailabilityContextRetain(const void *info)
{
return (__bridge_retained void *)((__bridge id)info);
}
static void MTNetworkAvailabilityContextRelease(const void *info)
{
void *retainedThing = (__bridge void *)((__bridge id)info);
__unused id unretainedThing = (__bridge_transfer id)retainedThing;
unretainedThing = nil;
}
@interface MTNetworkAvailability ()
{
SCNetworkReachabilityRef _reachability;
MTTimer *_timer;
NSString *_lastReachabilityState;
}
@end
@implementation MTNetworkAvailability
- (instancetype)initWithDelegate:(id<MTNetworkAvailabilityDelegate>)delegate
{
self = [super init];
if (self != nil)
{
_delegate = delegate;
[[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
_reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
if (_reachability != NULL)
{
MTNetworkAvailabilityContext *availabilityContext = [[MTNetworkAvailabilityContext alloc] init];
availabilityContext.context = self;
SCNetworkReachabilityContext context = { 0, (__bridge void *)availabilityContext, &MTNetworkAvailabilityContextRetain, &MTNetworkAvailabilityContextRelease, NULL };
[self updateReachability:kSCNetworkReachabilityFlagsReachable notify:false];
[self updateFlags:true];
__weak MTNetworkAvailability *weakSelf = self;
_timer = [[MTTimer alloc] initWithTimeout:5.0 repeat:true completion:^
{
__strong MTNetworkAvailability *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf updateFlags:true];
}
} queue:[MTNetworkAvailability networkAvailabilityQueue].nativeQueue];
[_timer start];
if (SCNetworkReachabilitySetCallback(_reachability, &MTAvailabilityCallback, &context))
SCNetworkReachabilitySetDispatchQueue(_reachability, [MTNetworkAvailability networkAvailabilityQueue].nativeQueue);
}
}];
}
return self;
}
- (void)dealloc
{
SCNetworkReachabilityRef reachability = _reachability;
_reachability = nil;
MTTimer *timer = _timer;
_timer = nil;
[[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^{
[timer invalidate];
SCNetworkReachabilitySetCallback(reachability, NULL, NULL);
SCNetworkReachabilitySetDispatchQueue(reachability, NULL);
CFRelease(reachability);
}];
}
+ (MTQueue *)networkAvailabilityQueue
{
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[MTQueue alloc] initWithName:"org.mtproto.MTNetwotkAvailability"];
});
return queue;
}
- (void)updateFlags:(bool)notify
{
[[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
{
if (_reachability != nil)
{
SCNetworkReachabilityFlags currentFlags = 0;
if (SCNetworkReachabilityGetFlags(_reachability, &currentFlags))
[self updateReachability:currentFlags notify:notify];
}
}];
}
- (void)updateReachability:(SCNetworkReachabilityFlags)flags notify:(bool)notify
{
[[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^
{
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically &&
(flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
BOOL isNetworkReachable = isReachable;
bool isWWAN = false;
#if TARGET_OS_IPHONE
isWWAN = (flags & kSCNetworkReachabilityFlagsIsWWAN);
#endif
NSString *currentReachabilityState = [[NSString alloc] initWithFormat:@"%s_%s_%s", isWWAN ? "M" : "L", canConnectWithoutUserInteraction ? "+U" : "-U", isNetworkReachable ? "+" : "-"];
if (![currentReachabilityState isEqualToString:_lastReachabilityState])
{
_lastReachabilityState = currentReachabilityState;
if (MTLogEnabled()) {
MTLog(@"[MTNetworkAvailability#%p state: %@]", self, _lastReachabilityState);
}
if (notify)
{
id<MTNetworkAvailabilityDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(networkAvailabilityChanged:networkIsAvailable:)])
[delegate networkAvailabilityChanged:self networkIsAvailable:isNetworkReachable];
}
}
}];
}
@end
static void MTAvailabilityCallback(__unused SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
MTNetworkAvailabilityContext *availabilityContext = ((__bridge MTNetworkAvailabilityContext *)info);
MTNetworkAvailability *availability = availabilityContext.context;
[availability updateReachability:flags notify:true];
}
@@ -0,0 +1,17 @@
#import <MtProtoKit/MTNetworkUsageCalculationInfo.h>
@implementation MTNetworkUsageCalculationInfo
- (instancetype)initWithFilePath:(NSString *)filePath incomingWWANKey:(int32_t)incomingWWANKey outgoingWWANKey:(int32_t)outgoingWWANKey incomingOtherKey:(int32_t)incomingOtherKey outgoingOtherKey:(int32_t)outgoingOtherKey {
self = [super init];
if (self != nil) {
_filePath = filePath;
_incomingWWANKey = incomingWWANKey;
_outgoingWWANKey = outgoingWWANKey;
_incomingOtherKey = incomingOtherKey;
_outgoingOtherKey = outgoingOtherKey;
}
return self;
}
@end
@@ -0,0 +1,245 @@
#import <MtProtoKit/MTNetworkUsageManager.h>
#include <sys/mman.h>
#import <os/lock.h>
#import <MtProtoKit/MTNetworkUsageCalculationInfo.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTTimer.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTAtomic.h>
static int offsetForInterface(MTNetworkUsageCalculationInfo *info, MTNetworkUsageManagerInterface interface, bool incoming) {
switch (interface) {
case MTNetworkUsageManagerInterfaceWWAN:
if (incoming) {
return info.incomingWWANKey * 8;
} else {
return info.outgoingWWANKey * 8;
}
case MTNetworkUsageManagerInterfaceOther:
if (incoming) {
return info.incomingOtherKey * 8;
} else {
return info.outgoingOtherKey * 8;
}
}
}
@interface MTNetworkUsageManagerImpl : NSObject {
MTQueue *_queue;
MTNetworkUsageCalculationInfo *_info;
NSMutableDictionary<NSNumber *, NSNumber *> *_pendingIncomingBytes;
NSMutableDictionary<NSNumber *, NSNumber *> *_pendingOutgoingBytes;
MTTimer *_timer;
}
@end
@implementation MTNetworkUsageManagerImpl
- (instancetype)initWithQueue:(MTQueue *)queue info:(MTNetworkUsageCalculationInfo *)info {
self = [super init];
if (self != nil) {
_queue = queue;
_info = info;
_pendingIncomingBytes = [[NSMutableDictionary alloc] init];
_pendingOutgoingBytes = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[self sync];
}
- (void)scheduleSync {
if (_timer == nil) {
__weak MTNetworkUsageManagerImpl *weakSelf = self;
_timer = [[MTTimer alloc] initWithTimeout:1.0 repeat:false completion:^{
__strong MTNetworkUsageManagerImpl *strongSelf = weakSelf;
[strongSelf sync];
} queue:_queue.nativeQueue];
[_timer start];
}
}
- (void)sync {
[_timer invalidate];
_timer = nil;
int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd >= 0) {
[_pendingIncomingBytes enumerateKeysAndObjectsUsingBlock:^(NSNumber * nInterface, NSNumber *nValue, __unused BOOL *stop) {
off_t offset = offsetForInterface(_info, (MTNetworkUsageManagerInterface)[nInterface intValue], true);
lseek(fd, offset, SEEK_SET);
int64_t currentValue = 0;
read(fd, &currentValue, 8);
currentValue += (int64_t)[nValue intValue];
lseek(fd, offset, SEEK_SET);
write(fd, &currentValue, 8);
}];
[_pendingOutgoingBytes enumerateKeysAndObjectsUsingBlock:^(NSNumber * nInterface, NSNumber *nValue, __unused BOOL *stop) {
off_t offset = offsetForInterface(_info, (MTNetworkUsageManagerInterface)[nInterface intValue], false);
lseek(fd, offset, SEEK_SET);
int64_t currentValue = 0;
read(fd, &currentValue, 8);
currentValue += (int64_t)[nValue intValue];
lseek(fd, offset, SEEK_SET);
write(fd, &currentValue, 8);
}];
close(fd);
}
[_pendingIncomingBytes removeAllObjects];
[_pendingOutgoingBytes removeAllObjects];
}
- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface {
_pendingIncomingBytes[@(interface)] = @([_pendingIncomingBytes[@(interface)] unsignedIntegerValue] + incomingBytes);
[self scheduleSync];
}
- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface {
_pendingOutgoingBytes[@(interface)] = @([_pendingOutgoingBytes[@(interface)] unsignedIntegerValue] + outgoingBytes);
[self scheduleSync];
}
- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion {
[self sync];
int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd >= 0) {
for (NSNumber *nKey in keys) {
lseek(fd, [nKey intValue] * 8, SEEK_SET);
int64_t currentValue = 0;
write(fd, &currentValue, 8);
}
[setKeys enumerateKeysAndObjectsUsingBlock:^(NSNumber *nKey, NSNumber *nValue, __unused BOOL *stop) {
lseek(fd, [nKey intValue] * 8, SEEK_SET);
int64_t currentValue = [nValue longLongValue];
write(fd, &currentValue, 8);
}];
close(fd);
}
if (completion) {
completion();
}
}
- (NSDictionary *)currentStatsForKeys:(NSArray<NSNumber *> *)keys {
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
[self sync];
int32_t fd = open([_info.filePath UTF8String], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd >= 0) {
for (NSNumber *nKey in keys) {
lseek(fd, [nKey intValue] * 8, SEEK_SET);
int64_t currentValue = 0;
read(fd, &currentValue, 8);
result[nKey] = @(currentValue);
}
int64_t currentValue = 0;
read(fd, &currentValue, 8);
close(fd);
}
return result;
}
@end
@interface MTNetworkUsageManagerImplHolder: NSObject
@property (nonatomic) void *impl;
@property (nonatomic) bool deallocated;
@end
@implementation MTNetworkUsageManagerImplHolder
@end
@interface MTNetworkUsageManager () {
MTQueue *_queue;
MTAtomic *_holder;
}
@end
@implementation MTNetworkUsageManager
- (instancetype)initWithInfo:(MTNetworkUsageCalculationInfo *)info {
self = [super init];
if (self != nil) {
_queue = [[MTQueue alloc] init];
_holder = [[MTAtomic alloc] initWithValue:[[MTNetworkUsageManagerImplHolder alloc] init]];
[_queue dispatchOnQueue:^{
[_holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
if (!holder.deallocated) {
holder.impl = (void *)CFBridgingRetain([[MTNetworkUsageManagerImpl alloc] initWithQueue:_queue info:info]);
}
return nil;
}];
}];
}
return self;
}
- (void)dealloc {
MTAtomic *holder = _holder;
[holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
holder.deallocated = true;
return nil;
}];
[_queue dispatchOnQueue:^{
__block void *impl = nil;
[holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
impl = holder.impl;
holder.impl = nil;
return nil;
}];
CFBridgingRelease(impl);
}];
}
- (void)with:(void (^)(MTNetworkUsageManagerImpl *))f {
[_queue dispatchOnQueue:^{
__block __strong MTNetworkUsageManagerImpl *impl = nil;
[_holder with:^id (MTNetworkUsageManagerImplHolder *holder) {
impl = (__bridge MTNetworkUsageManagerImpl *)holder.impl;
return nil;
}];
f(impl);
}];
}
- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface {
[self with:^(MTNetworkUsageManagerImpl *impl) {
[impl addIncomingBytes:incomingBytes interface:interface];
}];
}
- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface {
[self with:^(MTNetworkUsageManagerImpl *impl) {
[impl addOutgoingBytes:outgoingBytes interface:interface];
}];
}
- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion {
[self with:^(MTNetworkUsageManagerImpl *impl) {
[impl resetKeys:keys setKeys:setKeys completion:completion];
}];
}
- (MTSignal *)currentStatsForKeys:(NSArray<NSNumber *> *)keys {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
[self with:^(MTNetworkUsageManagerImpl *impl) {
NSDictionary *result = [impl currentStatsForKeys:keys];
[subscriber putNext:result];
[subscriber putCompletion];
}];
return nil;
}];
}
@end
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface MTNewSessionCreatedMessage : NSObject
@property (nonatomic, readonly) int64_t firstMessageId;
@property (nonatomic, readonly) int64_t uniqueId;
@property (nonatomic, readonly) int64_t serverSalt;
- (instancetype)initWithFirstMessageId:(int64_t)firstMessageId uniqueId:(int64_t)uniqueId serverSalt:(int64_t)serverSalt;
@end
@@ -0,0 +1,21 @@
#import "MTNewSessionCreatedMessage.h"
@implementation MTNewSessionCreatedMessage
- (instancetype)initWithFirstMessageId:(int64_t)firstMessageId uniqueId:(int64_t)uniqueId serverSalt:(int64_t)serverSalt
{
self = [super init];
if (self != nil)
{
_firstMessageId = firstMessageId;
_uniqueId = uniqueId;
_serverSalt = serverSalt;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"MTNewSessionCreatedMessage session_created firstMessageId:%lld uniqueId:%lld serverSalt: %lld", _firstMessageId, _uniqueId, _serverSalt];
}
@end
@@ -0,0 +1,73 @@
#import <MtProtoKit/MTOutgoingMessage.h>
#import <os/lock.h>
#import <libkern/OSAtomic.h>
@interface MTOutgoingMessageInternalId : NSObject <NSCopying>
{
NSUInteger _value;
}
@end
@implementation MTOutgoingMessageInternalId
- (instancetype)init
{
self = [super init];
if (self != nil)
{
static int32_t nextValue = 1;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
_value = OSAtomicIncrement32(&nextValue);
#pragma clang diagnostic pop
}
return self;
}
- (BOOL)isEqual:(id)object
{
return [object isKindOfClass:[MTOutgoingMessageInternalId class]] && ((MTOutgoingMessageInternalId *)object)->_value == _value;
}
- (NSUInteger)hash
{
return _value;
}
- (instancetype)copyWithZone:(NSZone *)__unused zone
{
MTOutgoingMessageInternalId *another = [[MTOutgoingMessageInternalId alloc] init];
if (another != nil)
another->_value = _value;
return another;
}
@end
@implementation MTOutgoingMessage
- (instancetype)initWithData:(NSData *)data metadata:(id)metadata additionalDebugDescription:(NSString *)additionalDebugDescription shortMetadata:(id)shortMetadata
{
return [self initWithData:data metadata:metadata additionalDebugDescription:additionalDebugDescription shortMetadata:shortMetadata messageId:0 messageSeqNo:0];
}
- (instancetype)initWithData:(NSData *)data metadata:(id)metadata additionalDebugDescription:(NSString *)additionalDebugDescription shortMetadata:(id)shortMetadata messageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo
{
self = [super init];
if (self != nil)
{
_internalId = [[MTOutgoingMessageInternalId alloc] init];
_data = data;
_metadata = metadata;
_additionalDebugDescription = additionalDebugDescription;
_shortMetadata = shortMetadata;
_messageId = messageId;
_messageSeqNo = messageSeqNo;
_requiresConfirmation = true;
}
return self;
}
@end
@@ -0,0 +1,172 @@
#import <MtProtoKit/MTOutputStream.h>
#if TARGET_OS_IPHONE
# import <endian.h>
#endif
static inline int roundUp(int numToRound, int multiple)
{
return multiple == 0 ? numToRound : ((numToRound % multiple) == 0 ? numToRound : (numToRound + multiple - (numToRound % multiple)));
}
@interface MTOutputStream ()
{
NSOutputStream *_wrappedOutputStream;
}
@end
@implementation MTOutputStream
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_wrappedOutputStream = [[NSOutputStream alloc] initToMemory];
[_wrappedOutputStream open];
}
return self;
}
- (NSOutputStream *)wrappedOutputStream
{
return _wrappedOutputStream;
}
- (void)dealloc
{
[_wrappedOutputStream close];
}
- (NSData *)currentBytes
{
return [_wrappedOutputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
}
- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)len
{
return [_wrappedOutputStream write:buffer maxLength:len];
}
- (void)writeInt32:(int32_t)value
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
[_wrappedOutputStream write:(const uint8_t *)&value maxLength:4];
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
}
- (void)writeInt64:(int64_t)value
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
[_wrappedOutputStream write:(const uint8_t *)&value maxLength:8];
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
}
- (void)writeDouble:(double)value
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
[_wrappedOutputStream write:(const uint8_t *)&value maxLength:8];
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
}
- (void)writeData:(NSData *)data
{
[_wrappedOutputStream write:(uint8_t *)data.bytes maxLength:data.length];
}
- (void)writeString:(NSString *)string
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
int32_t length = (int32_t)data.length;
if (data == nil || length == 0)
{
[self writeInt32:0];
return;
}
int paddingBytes = 0;
if (length >= 254)
{
uint8_t tmp = 254;
[_wrappedOutputStream write:&tmp maxLength:1];
#if __BYTE_ORDER == __LITTLE_ENDIAN
[_wrappedOutputStream write:(const uint8_t *)&length maxLength:3];
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
paddingBytes = roundUp(length, 4) - length;
}
else
{
[_wrappedOutputStream write:(const uint8_t *)&length maxLength:1];
paddingBytes = roundUp(length + 1, 4) - (length + 1);
}
[_wrappedOutputStream write:(uint8_t *)data.bytes maxLength:length];
uint8_t tmp = 0;
for (int i = 0; i < paddingBytes; i++)
[_wrappedOutputStream write:&tmp maxLength:1];
}
- (void)writeBytes:(NSData *)data
{
int32_t length = (int32_t)data.length;
if (data == nil || length == 0)
{
[self writeInt32:0];
return;
}
int paddingBytes = 0;
if (length >= 254)
{
uint8_t tmp = 254;
[_wrappedOutputStream write:&tmp maxLength:1];
#if __BYTE_ORDER == __LITTLE_ENDIAN
[_wrappedOutputStream write:(const uint8_t *)&length maxLength:3];
#elif __BYTE_ORDER == __BIG_ENDIAN
# error "Big endian is not implemented"
#else
# error "Unknown byte order"
#endif
paddingBytes = roundUp(length, 4) - length;
}
else
{
[_wrappedOutputStream write:(const uint8_t *)&length maxLength:1];
paddingBytes = roundUp(length + 1, 4) - (length + 1);
}
[_wrappedOutputStream write:(uint8_t *)data.bytes maxLength:length];
uint8_t tmp = 0;
for (int i = 0; i < paddingBytes; i++)
[_wrappedOutputStream write:&tmp maxLength:1];
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTPingMessage : NSObject
@property (nonatomic, readonly) int64_t pingId;
- (instancetype)initWithPingId:(int64_t)pingId;
@end
@@ -0,0 +1,15 @@
#import "MTPingMessage.h"
@implementation MTPingMessage
- (instancetype)initWithPingId:(int64_t)pingId
{
self = [super init];
if (self != nil)
{
_pingId = pingId;
}
return self;
}
@end
@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface MTPongMessage : NSObject
@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int64_t pingId;
- (instancetype)initWithMessageId:(int64_t)messageId pingId:(int64_t)pingId;
@end
@@ -0,0 +1,16 @@
#import "MTPongMessage.h"
@implementation MTPongMessage
- (instancetype)initWithMessageId:(int64_t)messageId pingId:(int64_t)pingId
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_pingId = pingId;
}
return self;
}
@end
@@ -0,0 +1,37 @@
#import <MtProtoKit/MTPreparedMessage.h>
#import <MtProtoKit/MTInternalId.h>
#import <libkern/OSAtomic.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
MTInternalIdClass(MTPreparedMessage)
#pragma clang diagnostic pop
@implementation MTPreparedMessage
- (instancetype)initWithData:(NSData *)data messageId:(int64_t)messageId seqNo:(int32_t)seqNo salt:(int64_t)salt requiresConfirmation:(bool)requiresConfirmation hasHighPriority:(bool)hasHighPriority
{
return [self initWithData:data messageId:messageId seqNo:seqNo salt:salt requiresConfirmation:requiresConfirmation hasHighPriority:hasHighPriority inResponseToMessageId:0];
}
- (instancetype)initWithData:(NSData *)data messageId:(int64_t)messageId seqNo:(int32_t)seqNo salt:(int64_t)salt requiresConfirmation:(bool)requiresConfirmation hasHighPriority:(bool)hasHighPriority inResponseToMessageId:(int64_t)inResponseToMessageId
{
self = [super init];
if (self != nil)
{
_internalId = [[MTInternalId(MTPreparedMessage) alloc] init];
_data = data;
_messageId = messageId;
_seqNo = seqNo;
_salt = salt;
_requiresConfirmation = requiresConfirmation;
_hasHighPriority = hasHighPriority;
_inResponseToMessageId = inResponseToMessageId;
}
return self;
}
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
#import <MtProtoKit/MTProtoEngine.h>
#import "Utils/MTQueueLocalObject.h"
#import <MtProtoKit/MTQueue.h>
NS_ASSUME_NONNULL_BEGIN
@interface MTProtoEngineImpl : NSObject {
MTQueue *_queue;
id<MTProtoPersistenceInterface> _persistenceInterface;
}
@end
@implementation MTProtoEngineImpl
- (instancetype)initWithQueue:(MTQueue *)queue persistenceInterface:(id<MTProtoPersistenceInterface>)persistenceInterface {
self = [super init];
if (self != nil) {
_queue = queue;
_persistenceInterface = persistenceInterface;
}
return self;
}
@end
@interface MTProtoEngine () {
MTQueue *_queue;
MTQueueLocalObject<MTProtoEngineImpl *> *_impl;
}
@end
@implementation MTProtoEngine
- (instancetype)initWithPersistenceInterface:(id<MTProtoPersistenceInterface>)persistenceInterface {
self = [super init];
if (self != nil) {
_queue = [[MTQueue alloc] init];
__auto_type queue = _queue;
_impl = [[MTQueueLocalObject<MTProtoEngineImpl
*> alloc] initWithQueue:queue generator:^MTProtoEngineImpl *{
return [[MTProtoEngineImpl alloc] initWithQueue:queue persistenceInterface:persistenceInterface];
}];
}
return self;
}
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,48 @@
#import <MtProtoKit/MTProtoInstance.h>
#import <MtProtoKit/MTQueue.h>
#import "Utils/MTQueueLocalObject.h"
@interface MTProtoInstanceImpl : NSObject {
MTQueue *_queue;
MTProtoEngine *_engine;
}
@end
@implementation MTProtoInstanceImpl
- (instancetype)initWithQueue:(MTQueue *)queue engine:(MTProtoEngine *)engine {
self = [super init];
if (self != nil) {
_queue = queue;
_engine = engine;
}
return self;
}
@end
@interface MTProtoInstance () {
MTQueue *_queue;
MTQueueLocalObject<MTProtoInstanceImpl *> *_impl;
}
@end
@implementation MTProtoInstance
- (instancetype)initWithEngine:(MTProtoEngine *)engine {
self = [super init];
if (self != nil) {
_queue = [[MTQueue alloc] init];
__auto_type queue = _queue;
_impl = [[MTQueueLocalObject<MTProtoInstanceImpl
*> alloc] initWithQueue:queue generator:^MTProtoInstanceImpl *{
return [[MTProtoInstanceImpl alloc] initWithQueue:queue engine:engine];
}];
}
return self;
}
@end
@@ -0,0 +1,153 @@
#import <MtProtoKit/MTProxyConnectivity.h>
#import <MtProtoKit/MTSignal.h>
#import <MtProtoKit/MTQueue.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTApiEnvironment.h>
#import <MtProtoKit/MTDatacenterAddressSet.h>
#import <MtProtoKit/MTDatacenterAddress.h>
#import "MTTcpConnection.h"
#import <MtProtoKit/MTTransportScheme.h>
#import "MTDiscoverConnectionSignals.h"
@implementation MTProxyConnectivityStatus
- (instancetype)initWithReachable:(bool)reachable roundTripTime:(NSTimeInterval)roundTripTime {
self = [super init];
if (self != nil) {
_reachable = reachable;
_roundTripTime = roundTripTime;
}
return self;
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxyConnectivityStatus class]]) {
return false;
}
MTProxyConnectivityStatus *other = object;
if (_reachable != other.reachable) {
return false;
}
if (_roundTripTime != other.roundTripTime) {
return false;
}
return true;
}
@end
@implementation MTProxyConnectivity
+ (bool)isResponseValid:(NSData *)data payloadData:(MTPayloadData)payloadData
{
if (data.length >= 84)
{
uint8_t zero[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
uint8_t resPq[] = { 0x63, 0x24, 0x16, 0x05 };
if (memcmp((uint8_t * const)data.bytes, zero, 8) == 0 && memcmp(((uint8_t * const)data.bytes) + 20, resPq, 4) == 0 && memcmp(((uint8_t * const)data.bytes) + 24, payloadData.nonce, 16) == 0)
{
return true;
}
}
return false;
}
+ (MTSignal *)pingWithAddress:(MTDatacenterAddress *)address datacenterId:(NSUInteger)datacenterId settings:(MTSocksProxySettings *)settings context:(MTContext *)context {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
MTQueue *queue = [[MTQueue alloc] init];
MTMetaDisposable *disposable = [[MTMetaDisposable alloc] init];
[queue dispatchOnQueue:^{
[subscriber putNext:[NSNull null]];
MTPayloadData payloadData;
NSData *data = [MTDiscoverConnectionSignals payloadData:&payloadData context:context address:address];
MTContext *proxyContext = [[MTContext alloc] initWithSerialization:context.serialization encryptionProvider:context.encryptionProvider apiEnvironment:[[context apiEnvironment] withUpdatedSocksProxySettings:settings] isTestingEnvironment:context.isTestingEnvironment useTempAuthKeys:false];
proxyContext.makeTcpConnectionInterface = context.makeTcpConnectionInterface;
MTTcpConnection *connection = [[MTTcpConnection alloc] initWithContext:proxyContext datacenterId:datacenterId scheme:[[MTTransportScheme alloc] initWithTransportClass:[MTTcpConnection class] address:address media:false] interface:nil usageCalculationInfo:nil getLogPrefix:nil];
__weak MTTcpConnection *weakConnection = connection;
__block NSTimeInterval startTime = CFAbsoluteTimeGetCurrent();
connection.connectionOpened = ^ {
__strong MTTcpConnection *strongConnection = weakConnection;
if (strongConnection != nil) {
startTime = CFAbsoluteTimeGetCurrent();
[strongConnection sendDatas:@[data] completion:nil requestQuickAck:false expectDataInResponse:true];
}
};
__block bool received = false;
connection.connectionReceivedData = ^(NSData *data) {
received = true;
if ([self isResponseValid:data payloadData:payloadData]) {
NSTimeInterval roundTripTime = CFAbsoluteTimeGetCurrent() - startTime;
[subscriber putNext:[[MTProxyConnectivityStatus alloc] initWithReachable:true roundTripTime:roundTripTime]];
} else {
[subscriber putNext:[[MTProxyConnectivityStatus alloc] initWithReachable:false roundTripTime:0.0]];
}
[subscriber putCompletion];
};
connection.connectionClosed = ^
{
if (!received) {
[subscriber putNext:[[MTProxyConnectivityStatus alloc] initWithReachable:false roundTripTime:0.0]];
[subscriber putCompletion];
}
};
[connection start];
[disposable setDisposable:[[MTBlockDisposable alloc] initWithBlock:^{
[queue dispatchOnQueue:^{
[connection stop];
__unused id desc = [proxyContext description];
}];
}]];
}];
return disposable;
}];
}
+ (MTSignal *)pingProxyWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId settings:(MTSocksProxySettings *)settings {
return [[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
MTDatacenterAddressSet *addressSet = [context addressSetForDatacenterWithId:datacenterId];
NSMutableArray *signals = [[NSMutableArray alloc] init];
for (MTDatacenterAddress *address in addressSet.addressList) {
if (!address.isIpv6) {
[signals addObject:[self pingWithAddress:address datacenterId:datacenterId settings:settings context:context]];
}
if (address.isIpv6) {
[signals addObject:[self pingWithAddress:address datacenterId:datacenterId settings:settings context:context]];
}
}
if (signals.count == 0) {
[subscriber putNext:[[MTProxyConnectivityStatus alloc] initWithReachable:false roundTripTime:0.0]];
[subscriber putCompletion];
return nil;
}
return [[MTSignal combineSignals:signals] startWithNext:^(NSArray *results) {
bool allStatusesAreValid = true;
for (MTProxyConnectivityStatus *status in results) {
if ([status isKindOfClass:[MTProxyConnectivityStatus class]]) {
if (status.reachable) {
[subscriber putNext:status];
[subscriber putCompletion];
return;
}
} else {
allStatusesAreValid = false;
}
}
if (allStatusesAreValid) {
[subscriber putNext:[[MTProxyConnectivityStatus alloc] initWithReachable:false roundTripTime:0.0]];
[subscriber putCompletion];
}
}];
}];
}
@end
+132
View File
@@ -0,0 +1,132 @@
#import <MtProtoKit/MTQueue.h>
@interface MTQueue ()
{
bool _isMainQueue;
dispatch_queue_t _queue;
const char *_name;
}
@end
@implementation MTQueue
- (instancetype)init {
self = [super init];
if (self != nil)
{
_queue = dispatch_queue_create(nil, 0);
}
return self;
}
- (instancetype)initWithName:(const char *)name
{
self = [super init];
if (self != nil)
{
_name = name;
_queue = dispatch_queue_create(_name, 0);
dispatch_queue_set_specific(_queue, _name, (void *)_name, NULL);
}
return self;
}
- (void)dealloc
{
#if !OS_OBJECT_HAVE_OBJC_SUPPORT
dispatch_release(_queue);
#endif
_queue = nil;
}
+ (MTQueue *)mainQueue
{
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[MTQueue alloc] init];
queue->_queue = dispatch_get_main_queue();
queue->_isMainQueue = true;
});
return queue;
}
+ (MTQueue *)concurrentDefaultQueue {
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[MTQueue alloc] init];
queue->_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
});
return queue;
}
+ (MTQueue *)concurrentLowQueue {
static MTQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^ {
queue = [[MTQueue alloc] init];
queue->_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
});
return queue;
}
- (dispatch_queue_t)nativeQueue
{
return _queue;
}
- (bool)isCurrentQueue
{
if (_queue == nil || _name == nil)
return false;
if (_isMainQueue)
return [NSThread isMainThread];
else
return dispatch_get_specific(_name) == _name;
}
- (void)dispatch:(dispatch_block_t)block {
[self dispatchOnQueue:block synchronous:false];
}
- (void)dispatchOnQueue:(dispatch_block_t)block
{
[self dispatchOnQueue:block synchronous:false];
}
- (void)dispatchOnQueue:(dispatch_block_t)block synchronous:(bool)synchronous
{
if (block == nil)
return;
if (_queue != nil)
{
if (_isMainQueue)
{
if ([NSThread isMainThread])
block();
else if (synchronous)
dispatch_sync(_queue, block);
else
dispatch_async(_queue, block);
}
else
{
if (_name != NULL && dispatch_get_specific(_name) == _name)
block();
else if (synchronous)
dispatch_sync(_queue, block);
else
dispatch_async(_queue, block);
}
}
}
@end
+86
View File
@@ -0,0 +1,86 @@
#import <MtProtoKit/MTRequest.h>
#import <MtProtoKit/MTRpcError.h>
#import <os/lock.h>
#import <libkern/OSAtomic.h>
@implementation MTRequestResponseInfo
- (instancetype)initWithNetworkType:(int32_t)networkType timestamp:(double)timestamp duration:(double)duration {
self = [super init];
if (self != nil) {
_networkType = networkType;
_timestamp = timestamp;
_duration = duration;
}
return self;
}
@end
@interface MTRequestInternalId : NSObject <NSCopying>
{
NSUInteger _value;
}
@end
@implementation MTRequestInternalId
- (instancetype)init
{
self = [super init];
if (self != nil)
{
static int32_t nextValue = 1;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
_value = OSAtomicIncrement32(&nextValue);
#pragma clang diagnostic pop
}
return self;
}
- (BOOL)isEqual:(id)object
{
return [object isKindOfClass:[MTRequestInternalId class]] && ((MTRequestInternalId *)object)->_value == _value;
}
- (NSUInteger)hash
{
return _value;
}
- (instancetype)copyWithZone:(NSZone *)__unused zone
{
MTRequestInternalId *another = [[MTRequestInternalId alloc] init];
if (another != nil)
another->_value = _value;
return another;
}
@end
@implementation MTRequest
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_internalId = [[MTRequestInternalId alloc] init];
_dependsOnPasswordEntry = true;
}
return self;
}
- (void)setPayload:(NSData *)payload metadata:(id)metadata shortMetadata:(id)shortMetadata responseParser:(id (^)(NSData *))responseParser
{
_payload = payload;
_metadata = metadata;
_shortMetadata = shortMetadata;
_responseParser = [responseParser copy];
}
@end
@@ -0,0 +1,18 @@
#import <MtProtoKit/MTRequestContext.h>
@implementation MTRequestContext
- (instancetype)initWithMessageId:(int64_t)messageId messageSeqNo:(int32_t)messageSeqNo transactionId:(id)transactionId quickAckId:(int32_t)quickAckId
{
self = [super init];
if (self != nil)
{
_messageId = messageId;
_messageSeqNo = messageSeqNo;
_transactionId = transactionId;
_quickAckId = quickAckId;
}
return self;
}
@end
@@ -0,0 +1,29 @@
#import <MtProtoKit/MTRequestErrorContext.h>
@implementation MTRequestPendingVerificationData
- (instancetype)initWithNonce:(NSString *)nonce {
self = [super init];
if (self != nil) {
_nonce = nonce;
}
return self;
}
@end
@implementation MTRequestPendingRecaptchaVerificationData
- (instancetype)initWithSiteKey:(NSString *)siteKey {
self = [super init];
if (self != nil) {
_siteKey = siteKey;
}
return self;
}
@end
@implementation MTRequestErrorContext
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface MTResPqMessage : NSObject
@property (nonatomic, strong, readonly) NSData *nonce;
@property (nonatomic, strong, readonly) NSData *serverNonce;
@property (nonatomic, strong, readonly) NSData *pq;
@property (nonatomic, strong, readonly) NSArray *serverPublicKeyFingerprints;
- (instancetype)initWithNonce:(NSData *)nonce serverNonce:(NSData *)serverNonce pq:(NSData *)pq serverPublicKeyFingerprints:(NSArray *)serverPublicKeyFingerprints;
@end
@@ -0,0 +1,29 @@
#import "MTResPqMessage.h"
@implementation MTResPqMessage
- (instancetype)initWithNonce:(NSData *)nonce serverNonce:(NSData *)serverNonce pq:(NSData *)pq serverPublicKeyFingerprints:(NSArray *)serverPublicKeyFingerprints
{
self = [super init];
if (self != nil)
{
_nonce = nonce;
_serverNonce = serverNonce;
_pq = pq;
_serverPublicKeyFingerprints = serverPublicKeyFingerprints;
}
return self;
}
- (NSString *)description {
NSMutableString *fingerprintsString = [[NSMutableString alloc] init];
for (NSNumber *value in _serverPublicKeyFingerprints) {
if (fingerprintsString.length != 0) {
[fingerprintsString appendString:@"\n"];
}
[fingerprintsString appendFormat:@"%llx", [value longLongValue]];
}
return [NSString stringWithFormat:@"res_pq nonce:%@ serverNonce:%@ pq:%@ fingerprints:%@", _nonce, _serverNonce, _pq, fingerprintsString];
}
@end
@@ -0,0 +1,142 @@
#import <MtProtoKit/MTResendMessageService.h>
#import <Foundation/Foundation.h>
#import <MtProtoKit/MTLogging.h>
#import <MtProtoKit/MTProto.h>
#import <MtProtoKit/MTContext.h>
#import <MtProtoKit/MTSerialization.h>
#import <MtProtoKit/MTMessageTransaction.h>
#import <MtProtoKit/MTOutgoingMessage.h>
#import <MtProtoKit/MTPreparedMessage.h>
#import <MtProtoKit/MTIncomingMessage.h>
#import "MTBuffer.h"
#import "MTMsgsStateInfoMessage.h"
@interface MTResendMessageService ()
{
int64_t _currentRequestMessageId;
id _currentRequestTransactionId;
}
@end
@implementation MTResendMessageService
- (instancetype)initWithMessageId:(int64_t)messageId
{
#ifdef DEBUG
NSAssert(messageId != 0, @"messageId should not be 0");
#endif
self = [super init];
if (self != nil)
{
_messageId = messageId;
}
return self;
}
- (void)mtProtoDidAddService:(MTProto *)mtProto
{
[mtProto requestTransportTransaction];
}
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector sessionInfo:(MTSessionInfo *)sessionInfo scheme:(MTTransportScheme *)scheme
{
if (_currentRequestMessageId == 0 || _currentRequestTransactionId == nil)
{
_currentRequestTransactionId = nil;
MTBuffer *resendRequestBuffer = [[MTBuffer alloc] init];
[resendRequestBuffer appendInt32:(int32_t)0x7d861a08];
[resendRequestBuffer appendInt32:481674261];
[resendRequestBuffer appendInt32:1];
[resendRequestBuffer appendInt64:_messageId];
NSData *resentMessagesRequestData = resendRequestBuffer.data;
MTOutgoingMessage *outgoingMessage = [[MTOutgoingMessage alloc] initWithData:resentMessagesRequestData metadata:@"resendMessages" additionalDebugDescription:nil shortMetadata:@"resendMessages"];
outgoingMessage.requiresConfirmation = false;
return [[MTMessageTransaction alloc] initWithMessagePayload:@[outgoingMessage] prepared:nil failed:nil completion:^(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, __unused NSDictionary *messageInternalIdToQuickAckId)
{
if ( messageInternalIdToTransactionId[outgoingMessage.internalId] != nil && messageInternalIdToPreparedMessage[outgoingMessage.internalId] != nil)
{
_currentRequestMessageId = ((MTPreparedMessage *)messageInternalIdToPreparedMessage[outgoingMessage.internalId]).messageId;
_currentRequestTransactionId = messageInternalIdToTransactionId[outgoingMessage.internalId];
if (MTLogEnabled()) {
MTLog(@"[MTResendMessageService#%p request %" PRId64 " for %" PRId64 "]", self, _currentRequestMessageId, _messageId);
}
}
}];
}
return nil;
}
- (void)mtProto:(MTProto *)mtProto messageDeliveryFailed:(int64_t)messageId
{
if (messageId == _currentRequestMessageId)
{
_currentRequestMessageId = 0;
_currentRequestTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProtoDidChangeSession:(MTProto *)__unused mtProto
{
id<MTResendMessageServiceDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(resendMessageServiceCompleted:)])
[delegate resendMessageServiceCompleted:self];
}
- (void)mtProtoServerDidChangeSession:(MTProto *)__unused mtProto firstValidMessageId:(int64_t)firstValidMessageId otherValidMessageIds:(NSArray *)otherValidMessageIds
{
if (_currentRequestMessageId != 0 && _currentRequestMessageId < firstValidMessageId && ![otherValidMessageIds containsObject:@(_currentRequestMessageId)])
{
id<MTResendMessageServiceDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(resendMessageServiceCompleted:)])
[delegate resendMessageServiceCompleted:self];
}
}
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds
{
if (_currentRequestTransactionId != nil && [transactionIds containsObject:_currentRequestTransactionId])
{
_currentRequestTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto
{
if (_currentRequestTransactionId != nil)
{
_currentRequestTransactionId = nil;
[mtProto requestTransportTransaction];
}
}
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message authInfoSelector:(MTDatacenterAuthInfoSelector)authInfoSelector networkType:(int32_t)networkType
{
if (message.messageId == _messageId)
{
id<MTResendMessageServiceDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(resendMessageServiceCompleted:)])
[delegate resendMessageServiceCompleted:self];
}
else if ([message.body isKindOfClass:[MTMsgsStateInfoMessage class]] && ((MTMsgsStateInfoMessage *)message.body).requestMessageId == _currentRequestMessageId)
{
[mtProto _messageResendRequestFailed:_messageId];
id<MTResendMessageServiceDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(resendMessageServiceCompleted:)])
[delegate resendMessageServiceCompleted:self];
}
}
@end
@@ -0,0 +1,20 @@
#import <MtProtoKit/MTRpcError.h>
@implementation MTRpcError
- (instancetype)initWithErrorCode:(int32_t)errorCode errorDescription:(NSString *)errorDescription
{
self = [super init];
if (self != nil)
{
_errorCode = errorCode;
_errorDescription = errorDescription;
}
return self;
}
- (NSString *)description {
return [[NSString alloc] initWithFormat:@"%d: %@", _errorCode, _errorDescription];
}
@end
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@class MTRpcError;
@interface MTRpcResultMessage : NSObject
@property (nonatomic, readonly) int64_t requestMessageId;
@property (nonatomic, strong, readonly) NSData *data;
- (instancetype)initWithRequestMessageId:(int64_t)requestMessagId data:(NSData *)data;
@end
@@ -0,0 +1,16 @@
#import "MTRpcResultMessage.h"
@implementation MTRpcResultMessage
- (instancetype)initWithRequestMessageId:(int64_t)requestMessagId data:(NSData *)data
{
self = [super init];
if (self != nil)
{
_requestMessageId = requestMessagId;
_data = data;
}
return self;
}
@end
+22
View File
@@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
#if TARGET_OS_IOS
@interface MTRsa : NSObject
// return base64 encoded string
+ (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey;
// return raw data
+ (NSData *)encryptData:(NSData *)data publicKey:(NSString *)pubKey;
// return base64 encoded string
// enc with private key NOT working YET!
//+ (NSString *)encryptString:(NSString *)str privateKey:(NSString *)privKey;
// return raw data
//+ (NSData *)encryptData:(NSData *)data privateKey:(NSString *)privKey;
// decrypt base64 encoded string, convert result to string(not base64 encoded)
+ (NSString *)decryptString:(NSString *)str publicKey:(NSString *)pubKey;
+ (NSData *)decryptData:(NSData *)data publicKey:(NSString *)pubKey;
+ (NSString *)decryptString:(NSString *)str privateKey:(NSString *)privKey;
+ (NSData *)decryptData:(NSData *)data privateKey:(NSString *)privKey;
@end
#endif

Some files were not shown because too many files have changed in this diff Show More