Add 'submodules/MtProtoKit/' from commit '14ab734b977fd4f1686a2a13415f6a4c9b9fdd6d'

git-subtree-dir: submodules/MtProtoKit
git-subtree-mainline: 3b155750f5a4894ff3dedf1860a37e94e0ea9571
git-subtree-split: 14ab734b977fd4f1686a2a13415f6a4c9b9fdd6d
This commit is contained in:
Peter 2019-06-11 18:55:34 +01:00
commit 373769682e
294 changed files with 77352 additions and 0 deletions

25
submodules/MtProtoKit/.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
fastlane/README.md
fastlane/report.xml
fastlane/test_output/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.xcscmblueprint
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
.DS_Store
*.dSYM
*.dSYM.zip
*.ipa
*/xcuserdata/*
MtProtoKit.xcodeproj/*

119
submodules/MtProtoKit/BUCK Normal file
View File

@ -0,0 +1,119 @@
load('//tools:buck_utils.bzl', 'config_with_updated_linker_flags', 'combined_config', 'configs_with_config')
load('//tools:buck_defs.bzl', 'SHARED_CONFIGS', 'EXTENSION_LIB_SPECIFIC_CONFIG')
genrule(
name = 'openssl_libs',
srcs = [
'openssl/iOS/libcrypto.a',
],
bash = 'mkdir -p $OUT; cp $SRCS $OUT/',
out = 'openssl_libs',
visibility = [
'//submodules/MtProtoKit:...',
]
)
apple_library(
name = 'openssl',
visibility = [
'//submodules/MtProtoKit:MtProtoKit'
],
header_namespace = 'openssl',
exported_headers = glob([
'openssl/**/*.h',
]),
exported_linker_flags = [
'-lcrypto',
'-L$(location :openssl_libs)',
],
)
apple_library(
name = 'MtProtoKit',
srcs = glob([
'*.m',
'MtProtoKit/*.m',
'thirdparty/AFNetworking/*.m',
'thirdparty/AsyncSocket/*.m',
]),
configs = configs_with_config(combined_config([SHARED_CONFIGS, EXTENSION_LIB_SPECIFIC_CONFIG])),
headers = glob([
'*.h',
'MtProtoKit/*.h',
'thirdparty/AFNetworking/*.h',
'thirdparty/AsyncSocket/*.h',
]),
header_namespace = 'MtProtoKit',
exported_headers = [
'MtProtoKit/MTTime.h',
'MtProtoKit/MTTimer.h',
'MtProtoKit/MTLogging.h',
'MtProtoKit/MTEncryption.h',
'MtProtoKit/MTInternalId.h',
'MtProtoKit/MTQueue.h',
'MtProtoKit/MTOutputStream.h',
'MtProtoKit/MTInputStream.h',
'MtProtoKit/MTSerialization.h',
'MtProtoKit/MTExportedAuthorizationData.h',
'MtProtoKit/MTRpcError.h',
'MtProtoKit/MTKeychain.h',
'MtProtoKit/MTFileBasedKeychain.h',
'MtProtoKit/MTContext.h',
'MtProtoKit/MTTransportScheme.h',
'MtProtoKit/MTDatacenterTransferAuthAction.h',
'MtProtoKit/MTDatacenterAuthAction.h',
'MtProtoKit/MTDatacenterAuthMessageService.h',
'MtProtoKit/MTDatacenterAddress.h',
'MtProtoKit/MTDatacenterAddressSet.h',
'MtProtoKit/MTDatacenterAuthInfo.h',
'MtProtoKit/MTDatacenterSaltInfo.h',
'MtProtoKit/MTDatacenterAddressListData.h',
'MtProtoKit/MTProto.h',
'MtProtoKit/MTSessionInfo.h',
'MtProtoKit/MTTimeFixContext.h',
'MtProtoKit/MTPreparedMessage.h',
'MtProtoKit/MTOutgoingMessage.h',
'MtProtoKit/MTIncomingMessage.h',
'MtProtoKit/MTMessageEncryptionKey.h',
'MtProtoKit/MTMessageService.h',
'MtProtoKit/MTMessageTransaction.h',
'MtProtoKit/MTTimeSyncMessageService.h',
'MtProtoKit/MTRequestMessageService.h',
'MtProtoKit/MTRequest.h',
'MtProtoKit/MTRequestContext.h',
'MtProtoKit/MTRequestErrorContext.h',
'MtProtoKit/MTDropResponseContext.h',
'MtProtoKit/MTApiEnvironment.h',
'MtProtoKit/MTResendMessageService.h',
'MtProtoKit/MTNetworkAvailability.h',
'MtProtoKit/MTTransport.h',
'MtProtoKit/MTTransportTransaction.h',
'MtProtoKit/MTTcpTransport.h',
'MtProtoKit/MTHttpRequestOperation.h',
'MTAtomic.h',
'MTBag.h',
'MTDisposable.h',
'MTSubscriber.h',
'MTSignal.h',
'MTNetworkUsageCalculationInfo.h',
'MTNetworkUsageManager.h',
'MTBackupAddressSignals.h',
'thirdparty/AFNetworking/AFURLConnectionOperation.h',
'thirdparty/AFNetworking/AFHTTPRequestOperation.h',
'MTProxyConnectivity.h',
'MTGzip.h',
'MTDatacenterVerificationData.h',
'MTPKCS.h',
],
modular = True,
compiler_flags = ['-w'],
preprocessor_flags = ['-fobjc-arc'],
visibility = ['PUBLIC'],
deps = [
':openssl',
],
frameworks = [
'$SDKROOT/System/Library/Frameworks/Foundation.framework',
'$SDKROOT/System/Library/Frameworks/Security.framework',
],
)

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTEncryption.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTEncryption.h>
#else
# import <MtProtoKit/MTEncryption.h>
#endif
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);

View File

@ -0,0 +1,316 @@
#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) {
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);
}
}
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 && 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

View File

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface MTAtomic : NSObject
- (instancetype)initWithValue:(id)value;
- (id)swap:(id)newValue;
- (id)value;
- (id)modify:(id (^)(id))f;
- (id)with:(id (^)(id))f;
@end

View File

@ -0,0 +1,64 @@
#import "MTAtomic.h"
#import <libkern/OSAtomic.h>
@interface MTAtomic ()
{
volatile OSSpinLock _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;
OSSpinLockLock(&_lock);
previousValue = _value;
_value = newValue;
OSSpinLockUnlock(&_lock);
return previousValue;
}
- (id)value
{
id previousValue = nil;
OSSpinLockLock(&_lock);
previousValue = _value;
OSSpinLockUnlock(&_lock);
return previousValue;
}
- (id)modify:(id (^)(id))f
{
id newValue = nil;
OSSpinLockLock(&_lock);
newValue = f(_value);
_value = newValue;
OSSpinLockUnlock(&_lock);
return newValue;
}
- (id)with:(id (^)(id))f
{
id result = nil;
OSSpinLockLock(&_lock);
result = f(_value);
OSSpinLockUnlock(&_lock);
return result;
}
@end

View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@class MTSignal;
@class MTContext;
@interface MTBackupAddressSignals : NSObject
+ (MTSignal * _Nonnull)fetchBackupIps:(bool)isTestingEnvironment currentContext:(MTContext * _Nonnull)currentContext additionalSource:(MTSignal * _Nullable)additionalSource phoneNumber:(NSString * _Nullable)phoneNumber;
@end

View File

@ -0,0 +1,231 @@
#import "MTBackupAddressSignals.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTSignal.h>
# import <MTProtoKitDynamic/MTQueue.h>
# import <MTProtoKitDynamic/MTHttpRequestOperation.h>
# import <MTProtoKitDynamic/MTEncryption.h>
# import <MTProtoKitDynamic/MTRequestMessageService.h>
# import <MTProtoKitDynamic/MTRequest.h>
# import <MTProtoKitDynamic/MTContext.h>
# import <MTProtoKitDynamic/MTApiEnvironment.h>
# import <MTProtoKitDynamic/MTDatacenterAddress.h>
# import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
# import <MTProtoKitDynamic/MTProto.h>
# import <MTProtoKitDynamic/MTSerialization.h>
# import <MTProtoKitDynamic/MTLogging.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTSignal.h>
# import <MTProtoKitMac/MTQueue.h>
# import <MTProtoKitMac/MTHttpRequestOperation.h>
# import <MTProtoKitMac/MTEncryption.h>
# import <MTProtoKitMac/MTRequestMessageService.h>
# import <MTProtoKitMac/MTRequest.h>
# import <MTProtoKitMac/MTContext.h>
# import <MTProtoKitMac/MTApiEnvironment.h>
# import <MTProtoKitMac/MTDatacenterAddress.h>
# import <MTProtoKitMac/MTDatacenterAddressSet.h>
# import <MTProtoKitMac/MTProto.h>
# import <MTProtoKitMac/MTSerialization.h>
# import <MTProtoKitMac/MTLogging.h>
#else
# import <MtProtoKit/MTSignal.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>
#endif
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 {
NSArray *hosts = @[
@"google.com",
@"www.google.com",
@"google.ru"
];
NSDictionary *headers = @{@"Host": @"dns.google.com"};
NSMutableArray *signals = [[NSMutableArray alloc] init];
for (NSString *host in hosts) {
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16", host, isTesting ? @"tapv2.stel.com" : @"apv2.stel.com"]] headers:headers] mapToSignal:^MTSignal *(NSData *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(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];
}
+ (MTSignal *)fetchConfigFromAddress:(MTBackupDatacenterAddress *)address currentContext:(MTContext *)currentContext {
MTApiEnvironment *apiEnvironment = [currentContext.apiEnvironment copy];
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 apiEnvironment:apiEnvironment isTestingEnvironment:currentContext.isTestingEnvironment useTempAuthKeys:address.datacenterId != 0 ? currentContext.useTempAuthKeys : false];
if (address.datacenterId != 0) {
//context.keychain = currentContext.keychain;
}
MTProto *mtProto = [[MTProto alloc] initWithContext:context datacenterId:address.datacenterId usageCalculationInfo:nil];
if (address.datacenterId != 0) {
mtProto.useTempAuthKeys = currentContext.useTempAuthKeys;
}
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 NSTimeInterval completionTimestamp, 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 {
NSMutableArray *signals = [[NSMutableArray alloc] init];
[signals addObject:[self fetchBackupIpsResolveGoogle:isTestingEnvironment phoneNumber:phoneNumber currentContext:currentContext]];
if (additionalSource != nil) {
[signals addObject:additionalSource];
}
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];
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

View File

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface MTBag : NSObject
- (NSInteger)addItem:(id)item;
- (void)enumerateItems:(void (^)(id))block;
- (void)removeItem:(NSInteger)key;
- (bool)isEmpty;
- (NSArray *)copyItems;
@end

View File

@ -0,0 +1,74 @@
#import "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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,177 @@
#import "MTConnectionProbing.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTSignal.h>
# import <MTProtoKitDynamic/MTQueue.h>
# import <MTProtoKitDynamic/MTAtomic.h>
# import <MTProtoKitDynamic/MTHttpRequestOperation.h>
# import <MTProtoKitDynamic/MTEncryption.h>
# import <MTProtoKitDynamic/MTRequestMessageService.h>
# import <MTProtoKitDynamic/MTRequest.h>
# import <MTProtoKitDynamic/MTContext.h>
# import <MTProtoKitDynamic/MTApiEnvironment.h>
# import <MTProtoKitDynamic/MTDatacenterAddress.h>
# import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
# import <MTProtoKitDynamic/MTProto.h>
# import <MTProtoKitDynamic/MTSerialization.h>
# import <MTProtoKitDynamic/MTLogging.h>
# import <MTProtoKitDynamic/MTProxyConnectivity.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTSignal.h>
# import <MTProtoKitMac/MTQueue.h>
# import <MTProtoKitMac/MTAtomic.h>
# import <MTProtoKitMac/MTHttpRequestOperation.h>
# import <MTProtoKitMac/MTEncryption.h>
# import <MTProtoKitMac/MTRequestMessageService.h>
# import <MTProtoKitMac/MTRequest.h>
# import <MTProtoKitMac/MTContext.h>
# import <MTProtoKitMac/MTApiEnvironment.h>
# import <MTProtoKitMac/MTDatacenterAddress.h>
# import <MTProtoKitMac/MTDatacenterAddressSet.h>
# import <MTProtoKitMac/MTProto.h>
# import <MTProtoKitMac/MTSerialization.h>
# import <MTProtoKitMac/MTLogging.h>
# import <MTProtoKitMac/MTProxyConnectivity.h>
#else
# 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>
#endif
#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

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

View File

@ -0,0 +1,372 @@
#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>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTQueue.h>
# import <MTProtoKitDynamic/MTSignal.h>
# import <MTProtoKitDynamic/MTBag.h>
# import <MTProtoKitDynamic/MTAtomic.h>
# import <MTProtoKitDynamic/MTHttpRequestOperation.h>
# import <MTProtoKitDynamic/MTEncryption.h>
# import <MTProtoKitDynamic/MTRequestMessageService.h>
# import <MTProtoKitDynamic/MTRequest.h>
# import <MTProtoKitDynamic/MTContext.h>
# import <MTProtoKitDynamic/MTApiEnvironment.h>
# import <MTProtoKitDynamic/MTDatacenterAddress.h>
# import <MTProtoKitDynamic/MTDatacenterAddressSet.h>
# import <MTProtoKitDynamic/MTProto.h>
# import <MTProtoKitDynamic/MTSerialization.h>
# import <MTProtoKitDynamic/MTLogging.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTQueue.h>
# import <MTProtoKitMac/MTSignal.h>
# import <MTProtoKitMac/MTBag.h>
# import <MTProtoKitMac/MTAtomic.h>
# import <MTProtoKitMac/MTHttpRequestOperation.h>
# import <MTProtoKitMac/MTEncryption.h>
# import <MTProtoKitMac/MTRequestMessageService.h>
# import <MTProtoKitMac/MTRequest.h>
# import <MTProtoKitMac/MTContext.h>
# import <MTProtoKitMac/MTApiEnvironment.h>
# import <MTProtoKitMac/MTDatacenterAddress.h>
# import <MTProtoKitMac/MTDatacenterAddressSet.h>
# import <MTProtoKitMac/MTProto.h>
# import <MTProtoKitMac/MTSerialization.h>
# import <MTProtoKitMac/MTLogging.h>
#else
# 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>
#endif
#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]] startWithNext:^(NSString *result) {
__strong MTDNSContext *strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
if (strongSelf->_contexts[key] != nil) {
[strongSelf->_contexts[key] complete:result];
[strongSelf->_contexts removeObjectForKey:key];
}
}]];
}
__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 *(NSData *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

View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface MTDatacenterVerificationData : NSObject
@property (nonatomic, readonly) NSInteger datacenterId;
@property (nonatomic, readonly) bool isTestingEnvironment;
- (instancetype _Nonnull)initWithDatacenterId:(NSInteger)datacenterId isTestingEnvironment:(bool)isTestingEnvironment;
@end

View File

@ -0,0 +1,18 @@
#import "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

View File

@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>
@class MTContext;
@class MTDatacenterAddress;
@class MTSignal;
typedef struct {
uint8_t nonce[16];
} MTPayloadData;
@interface MTDiscoverConnectionSignals : NSObject
+ (NSData *)payloadData:(MTPayloadData *)outPayloadData context:(MTContext *)context address:(MTDatacenterAddress *)address;
+ (MTSignal *)discoverSchemeWithContext:(MTContext *)context datacenterId:(NSInteger)datacenterId addressList:(NSArray *)addressList media:(bool)media isProxy:(bool)isProxy;
@end

View File

@ -0,0 +1,268 @@
#import "MTDiscoverConnectionSignals.h"
#import "MTTcpConnection.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTTransportScheme.h>
# import <MTProtoKitDynamic/MTTcpTransport.h>
# import <MTProtoKitDynamic/MTQueue.h>
# import <MTProtoKitDynamic/MTProtoKitDynamic.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTTransportScheme.h>
# import <MTProtoKitMac/MTTcpTransport.h>
# import <MTProtoKitMac/MTQueue.h>
# import <MTProtoKitMac/MTProtoKitMac.h>
#else
# import <MtProtoKit/MTTransportScheme.h>
# import <MtProtoKit/MTTcpTransport.h>
# import <MtProtoKit/MTQueue.h>
# import <MtProtoKit/MTProtoKit.h>
#endif
#import "MTDatacenterAddress.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTDisposable.h>
# import <MTProtoKitDynamic/MTSignal.h>
# import <MTProtoKitDynamic/MTAtomic.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTDisposable.h>
# import <MTProtoKitMac/MTSignal.h>
# import <MTProtoKitMac/MTAtomic.h>
#else
# import <MtProtoKit/MTDisposable.h>
# import <MtProtoKit/MTSignal.h>
# import <MtProtoKit/MTAtomic.h>
#endif
#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) {
if ([MTSocksProxySettings secretSupportsExtendedPadding:secret]) {
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];
__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];
}];
}
@end

View File

@ -0,0 +1,26 @@
#import <Foundation/Foundation.h>
@protocol MTDisposable <NSObject>
- (void)dispose;
@end
@interface MTBlockDisposable : NSObject <MTDisposable>
- (instancetype)initWithBlock:(void (^)())block;
@end
@interface MTMetaDisposable : NSObject <MTDisposable>
- (void)setDisposable:(id<MTDisposable>)disposable;
@end
@interface MTDisposableSet : NSObject <MTDisposable>
- (void)add:(id<MTDisposable>)disposable;
- (void)remove:(id<MTDisposable>)disposable;
@end

View File

@ -0,0 +1,198 @@
#import "MTDisposable.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
@interface MTBlockDisposable ()
{
void *_block;
}
@end
@implementation MTBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_block = (__bridge_retained void *)[block copy];
}
return self;
}
- (void)dealloc
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
strongBlock = nil;
}
}
}
}
- (void)dispose
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
((dispatch_block_t)strongBlock)();
strongBlock = nil;
}
}
}
}
@end
@interface MTMetaDisposable ()
{
OSSpinLock _lock;
bool _disposed;
id<MTDisposable> _disposable;
}
@end
@implementation MTMetaDisposable
- (void)setDisposable:(id<MTDisposable>)disposable
{
id<MTDisposable> previousDisposable = nil;
bool dispose = false;
OSSpinLockLock(&_lock);
dispose = _disposed;
if (!dispose)
{
previousDisposable = _disposable;
_disposable = disposable;
}
OSSpinLockUnlock(&_lock);
if (previousDisposable != nil)
[previousDisposable dispose];
if (dispose)
[disposable dispose];
}
- (void)dispose
{
id<MTDisposable> disposable = nil;
OSSpinLockLock(&_lock);
if (!_disposed)
{
disposable = _disposable;
_disposed = true;
}
OSSpinLockUnlock(&_lock);
if (disposable != nil)
[disposable dispose];
}
@end
@interface MTDisposableSet ()
{
OSSpinLock _lock;
bool _disposed;
id<MTDisposable> _singleDisposable;
NSArray *_multipleDisposables;
}
@end
@implementation MTDisposableSet
- (void)add:(id<MTDisposable>)disposable
{
if (disposable == nil)
return;
bool dispose = false;
OSSpinLockLock(&_lock);
dispose = _disposed;
if (!dispose)
{
if (_multipleDisposables != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
[multipleDisposables addObject:disposable];
_multipleDisposables = multipleDisposables;
}
else if (_singleDisposable != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithObjects:_singleDisposable, disposable, nil];
_multipleDisposables = multipleDisposables;
_singleDisposable = nil;
}
else
{
_singleDisposable = disposable;
}
}
OSSpinLockUnlock(&_lock);
if (dispose)
[disposable dispose];
}
- (void)remove:(id<MTDisposable>)disposable {
OSSpinLockLock(&_lock);
if (_multipleDisposables != nil)
{
NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
[multipleDisposables removeObject:disposable];
_multipleDisposables = multipleDisposables;
}
else if (_singleDisposable == disposable)
{
_singleDisposable = nil;
}
OSSpinLockUnlock(&_lock);
}
- (void)dispose
{
id<MTDisposable> singleDisposable = nil;
NSArray *multipleDisposables = nil;
OSSpinLockLock(&_lock);
if (!_disposed)
{
_disposed = true;
singleDisposable = _singleDisposable;
multipleDisposables = _multipleDisposables;
_singleDisposable = nil;
_multipleDisposables = nil;
}
OSSpinLockUnlock(&_lock);
if (singleDisposable != nil)
[singleDisposable dispose];
if (multipleDisposables != nil)
{
for (id<MTDisposable> disposable in multipleDisposables)
{
[disposable dispose];
}
}
}
@end

View File

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MTGzip : NSObject
+ (NSData * _Nullable)decompress:(NSData *)data;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,53 @@
#import "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;
unsigned char output[kMemoryChunkSize];
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);
}
@end

View File

@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
@class MTQueue;
@interface MTNetworkUsageCalculationInfo : NSObject
@property (nonatomic, strong, readonly) NSString *filePath;
@property (nonatomic, readonly) int32_t incomingWWANKey;
@property (nonatomic, readonly) int32_t outgoingWWANKey;
@property (nonatomic, readonly) int32_t incomingOtherKey;
@property (nonatomic, readonly) int32_t outgoingOtherKey;
- (instancetype)initWithFilePath:(NSString *)filePath incomingWWANKey:(int32_t)incomingWWANKey outgoingWWANKey:(int32_t)outgoingWWANKey incomingOtherKey:(int32_t)incomingOtherKey outgoingOtherKey:(int32_t)outgoingOtherKey;
@end

View File

@ -0,0 +1,17 @@
#import "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

View File

@ -0,0 +1,26 @@
#import <Foundation/Foundation.h>
@class MTSignal;
@class MTNetworkUsageCalculationInfo;
typedef enum {
MTNetworkUsageManagerInterfaceWWAN,
MTNetworkUsageManagerInterfaceOther
} MTNetworkUsageManagerInterface;
typedef struct {
NSUInteger incomingBytes;
NSUInteger outgoingBytes;
} MTNetworkUsageManagerInterfaceStats;
@interface MTNetworkUsageManager : NSObject
- (instancetype)initWithInfo:(MTNetworkUsageCalculationInfo *)info;
- (void)addIncomingBytes:(NSUInteger)incomingBytes interface:(MTNetworkUsageManagerInterface)interface;
- (void)addOutgoingBytes:(NSUInteger)outgoingBytes interface:(MTNetworkUsageManagerInterface)interface;
- (void)resetKeys:(NSArray<NSNumber *> *)keys setKeys:(NSDictionary<NSNumber *, NSNumber *> *)setKeys completion:(void (^)())completion;
- (MTSignal *)currentStatsForKeys:(NSArray<NSNumber *> *)keys;
@end

View File

@ -0,0 +1,259 @@
#import "MTNetworkUsageManager.h"
#include <sys/mman.h>
#import <libkern/OSAtomic.h>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTNetworkUsageCalculationInfo.h>
# import <MTProtoKitDynamic/MTSignal.h>
# import <MTProtoKitDynamic/MTTimer.h>
# import <MTProtoKitDynamic/MTQueue.h>
# import <MTProtoKitDynamic/MTAtomic.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTNetworkUsageCalculationInfo.h>
# import <MTProtoKitMac/MTSignal.h>
# import <MTProtoKitMac/MTTimer.h>
# import <MTProtoKitMac/MTQueue.h>
# import <MTProtoKitMac/MTAtomic.h>
#else
# import <MtProtoKit/MTNetworkUsageCalculationInfo.h>
# import <MtProtoKit/MTSignal.h>
# import <MtProtoKit/MTTimer.h>
# import <MtProtoKit/MTQueue.h>
# import <MtProtoKit/MTAtomic.h>
#endif
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

View File

@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MTPKCS : NSObject
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, strong, readonly) NSData *data;
+ (MTPKCS * _Nullable)parse:(const unsigned char *)buffer size:(int)size;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,59 @@
#import "MTPKCS.h"
#include <openssl/x509.h>
#include <openssl/pkcs7.h>
@implementation MTPKCS
- (instancetype)initWithName:(NSString *)name data:(NSData *)data {
self = [super init];
if (self != nil) {
_name = name;
_data = data;
}
return self;
}
+ (MTPKCS * _Nullable)parse:(const unsigned char *)buffer size:(int)size {
MTPKCS * _Nullable result = nil;
PKCS7 *pkcs7 = NULL;
STACK_OF(X509) *signers = NULL;
pkcs7 = d2i_PKCS7(NULL, &buffer, size);
if (pkcs7 == NULL) {
return nil;
}
if (!PKCS7_type_is_signed(pkcs7)) {
if (pkcs7) {
PKCS7_free(pkcs7);
}
return nil;
}
signers = PKCS7_get0_signers(pkcs7, NULL, PKCS7_BINARY);
if (signers == NULL) {
if (pkcs7) {
PKCS7_free(pkcs7);
}
return nil;
}
const X509* cert = sk_X509_pop(signers);
if (cert == NULL) {
if (signers) {
sk_X509_free(signers);
}
if (pkcs7) {
PKCS7_free(pkcs7);
}
return nil;
}
result = [[MTPKCS alloc] initWithName:[NSString stringWithUTF8String:cert->name] data:[NSData dataWithBytes:cert->cert_info->key->public_key->data length:cert->cert_info->key->public_key->length]];
return result;
}
@end

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,56 @@
#import <Foundation/Foundation.h>
@interface MTSocksProxySettings : NSObject
@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) uint16_t port;
@property (nonatomic, strong, readonly) NSString *username;
@property (nonatomic, strong, readonly) NSString *password;
@property (nonatomic, strong, readonly) NSData *secret;
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret;
+ (bool)secretSupportsExtendedPadding:(NSData *)data;
@end
@interface MTNetworkSettings : NSObject
@property (nonatomic, readonly) bool reducedBackupDiscoveryTimeout;
- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout;
@end
@interface MTApiEnvironment : NSObject
@property (nonatomic) int32_t apiId;
@property (nonatomic, strong, readonly) NSString *deviceModel;
@property (nonatomic, strong, readonly) NSString *systemVersion;
@property (nonatomic, strong) NSString *appVersion;
@property (nonatomic, strong, readonly) NSString *systemLangCode;
@property (nonatomic, strong) NSNumber *layer;
@property (nonatomic, strong, readonly) NSData *systemCode;
@property (nonatomic, strong) NSString *langPack;
@property (nonatomic, strong, readonly) NSString *langPackCode;
@property (nonatomic, strong, readonly) NSString *apiInitializationHash;
@property (nonatomic) bool disableUpdates;
@property (nonatomic) NSData *tcpPayloadPrefix;
@property (nonatomic) NSDictionary *datacenterAddressOverrides;
@property (nonatomic, strong, readonly) MTSocksProxySettings *socksProxySettings;
@property (nonatomic, strong, readonly) MTNetworkSettings *networkSettings;
@property (nonatomic, copy) void (^passwordInputHandler)(void);
- (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode;
- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings;
- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings;
- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode;
@end

View File

@ -0,0 +1,625 @@
#import "MTApiEnvironment.h"
#if TARGET_OS_IPHONE
# import <UIKit/UIKit.h>
#else
#endif
#include <sys/sysctl.h>
#import <CommonCrypto/CommonDigest.h>
#define IFPGA_NAMESTRING @"iFPGA"
#define IPHONE_1G_NAMESTRING @"iPhone 1G"
#define IPHONE_3G_NAMESTRING @"iPhone 3G"
#define IPHONE_3GS_NAMESTRING @"iPhone 3GS"
#define IPHONE_4_NAMESTRING @"iPhone 4"
#define IPHONE_4S_NAMESTRING @"iPhone 4S"
#define IPHONE_5_NAMESTRING @"iPhone 5"
#define IPHONE_5S_NAMESTRING @"iPhone 5S"
#define IPHONE_6_NAMESTRING @"iPhone 6"
#define IPHONE_6Plus_NAMESTRING @"iPhone 6 Plus"
#define IPHONE_6S_NAMESTRING @"iPhone 6S"
#define IPHONE_6SPlus_NAMESTRING @"iPhone 6S Plus"
#define IPHONE_7_NAMESTRING @"iPhone 7"
#define IPHONE_7Plus_NAMESTRING @"iPhone 7 Plus"
#define IPHONE_8_NAMESTRING @"iPhone 8"
#define IPHONE_8Plus_NAMESTRING @"iPhone 8 Plus"
#define IPHONE_X_NAMESTRING @"iPhone X"
#define IPHONE_SE_NAMESTRING @"iPhone SE"
#define IPHONE_UNKNOWN_NAMESTRING @"Unknown iPhone"
#define IPHONE_XS_NAMESTRING @"iPhone XS"
#define IPHONE_XSMAX_NAMESTRING @"iPhone XS Max"
#define IPHONE_XR_NAMESTRING @"iPhone XR"
#define IPOD_1G_NAMESTRING @"iPod touch 1G"
#define IPOD_2G_NAMESTRING @"iPod touch 2G"
#define IPOD_3G_NAMESTRING @"iPod touch 3G"
#define IPOD_4G_NAMESTRING @"iPod touch 4G"
#define IPOD_5G_NAMESTRING @"iPod touch 5G"
#define IPOD_6G_NAMESTRING @"iPod touch 6G"
#define IPOD_UNKNOWN_NAMESTRING @"Unknown iPod"
#define IPAD_1G_NAMESTRING @"iPad 1G"
#define IPAD_2G_NAMESTRING @"iPad 2G"
#define IPAD_3G_NAMESTRING @"iPad 3G"
#define IPAD_4G_NAMESTRING @"iPad 4G"
#define IPAD_5G_NAMESTRING @"iPad Air 2"
#define IPAD_6G_NAMESTRING @"iPad Pro"
#define IPAD_PRO_3G_NAMESTRING @"iPad Pro 12.9 (3rd gen)"
#define IPAD_PRO_11_NAMESTRING @"iPad Pro 11"
#define IPAD_PRO_6G_NAMESTRING @"iPad (6th gen)"
#define IPAD_PRO_10_5_NAMESTRING @"iPad Pro 10.5"
#define IPAD_PRO_12_9_NAMESTRING @"iPad Pro 12.9"
#define IPAD_UNKNOWN_NAMESTRING @"Unknown iPad"
#define APPLETV_2G_NAMESTRING @"Apple TV 2G"
#define APPLETV_3G_NAMESTRING @"Apple TV 3G"
#define APPLETV_4G_NAMESTRING @"Apple TV 4G"
#define APPLETV_UNKNOWN_NAMESTRING @"Unknown Apple TV"
#define IOS_FAMILY_UNKNOWN_DEVICE @"Unknown iOS device"
#define SIMULATOR_NAMESTRING @"iPhone Simulator"
#define SIMULATOR_IPHONE_NAMESTRING @"iPhone Simulator"
#define SIMULATOR_IPAD_NAMESTRING @"iPad Simulator"
#define SIMULATOR_APPLETV_NAMESTRING @"Apple TV Simulator" // :)
/*
iPad8,5, iPad8,6, iPad8,7, iPad8,8 - iPad Pro 12.9" (3rd gen)
iPad8,1, iPad8,2, iPad8,3, iPad8,4 - iPad Pro 11"
iPad7,5, iPad7,6 - iPad 6th gen
iPad7,3, iPad7,4 - iPad Pro 10.5"
iPad7,1, iPad7,2 - iPad Pro 12.9" (2ng gen)
*/
typedef enum {
UIDeviceUnknown,
UIDeviceSimulator,
UIDeviceSimulatoriPhone,
UIDeviceSimulatoriPad,
UIDeviceSimulatorAppleTV,
UIDevice1GiPhone,
UIDevice3GiPhone,
UIDevice3GSiPhone,
UIDevice4iPhone,
UIDevice4SiPhone,
UIDevice5iPhone,
UIDevice5SiPhone,
UIDevice6iPhone,
UIDevice6PlusiPhone,
UIDevice6siPhone,
UIDevice6SPlusiPhone,
UIDevice7iPhone,
UIDevice7PlusiPhone,
UIDevice8iPhone,
UIDevice8PlusiPhone,
UIDeviceXiPhone,
UIDeviceSEPhone,
UIDeviceXSiPhone,
UIDeviceXSMaxiPhone,
UIDeviceXRiPhone,
UIDevice1GiPod,
UIDevice2GiPod,
UIDevice3GiPod,
UIDevice4GiPod,
UIDevice5GiPod,
UIDevice6GiPod,
UIDevice1GiPad,
UIDevice2GiPad,
UIDevice3GiPad,
UIDevice4GiPad,
UIDevice5GiPad,
UIDevice6GiPad,
UIDeviceiPadPro12_93g,
UIDeviceiPadPro11,
UIDeviceiPadPro6g,
UIDeviceiPadPro10_5,
UIDeviceiPadPro12_9,
UIDeviceAppleTV2,
UIDeviceAppleTV3,
UIDeviceAppleTV4,
UIDeviceUnknowniPhone,
UIDeviceUnknowniPod,
UIDeviceUnknowniPad,
UIDeviceUnknownAppleTV,
UIDeviceIFPGA,
UIDeviceOSX
} UIDevicePlatform;
typedef enum {
UIDeviceFamilyiPhone,
UIDeviceFamilyiPod,
UIDeviceFamilyiPad,
UIDeviceFamilyAppleTV,
UIDeviceFamilyUnknown,
} UIDeviceFamily;
@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];
}
+ (bool)secretSupportsExtendedPadding:(NSData *)data {
if (data.length == 17) {
uint8_t first = 0;
[data getBytes:&first length:1];
return (first == 0xdd);
}
return false;
}
@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 = [super init];
if (self != nil)
{
_deviceModel = [self platformString];
#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
#ifdef BETA
suffix = @" BETA";
#endif
#ifdef APPSTORE
suffix = @" APPSTORE";
#endif
#ifdef STABLE
suffix = @" STABLE";
#endif
#endif
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
{
switch ([self platformType])
{
case UIDevice1GiPhone: return IPHONE_1G_NAMESTRING;
case UIDevice3GiPhone: return IPHONE_3G_NAMESTRING;
case UIDevice3GSiPhone: return IPHONE_3GS_NAMESTRING;
case UIDevice4iPhone: return IPHONE_4_NAMESTRING;
case UIDevice4SiPhone: return IPHONE_4S_NAMESTRING;
case UIDevice5iPhone: return IPHONE_5_NAMESTRING;
case UIDevice5SiPhone: return IPHONE_5S_NAMESTRING;
case UIDevice6iPhone: return IPHONE_6_NAMESTRING;
case UIDevice6PlusiPhone: return IPHONE_6Plus_NAMESTRING;
case UIDevice6siPhone: return IPHONE_6S_NAMESTRING;
case UIDevice6SPlusiPhone: return IPHONE_6SPlus_NAMESTRING;
case UIDevice7iPhone: return IPHONE_7_NAMESTRING;
case UIDevice7PlusiPhone: return IPHONE_7Plus_NAMESTRING;
case UIDevice8iPhone: return IPHONE_8_NAMESTRING;
case UIDevice8PlusiPhone: return IPHONE_8Plus_NAMESTRING;
case UIDeviceXiPhone: return IPHONE_X_NAMESTRING;
case UIDeviceSEPhone: return IPHONE_SE_NAMESTRING;
case UIDeviceXSiPhone: return IPHONE_XS_NAMESTRING;
case UIDeviceXSMaxiPhone: return IPHONE_XSMAX_NAMESTRING;
case UIDeviceXRiPhone: return IPHONE_XR_NAMESTRING;
case UIDeviceUnknowniPhone: return IPHONE_UNKNOWN_NAMESTRING;
case UIDevice1GiPod: return IPOD_1G_NAMESTRING;
case UIDevice2GiPod: return IPOD_2G_NAMESTRING;
case UIDevice3GiPod: return IPOD_3G_NAMESTRING;
case UIDevice4GiPod: return IPOD_4G_NAMESTRING;
case UIDevice5GiPod: return IPOD_5G_NAMESTRING;
case UIDevice6GiPod: return IPOD_6G_NAMESTRING;
case UIDeviceUnknowniPod: return IPOD_UNKNOWN_NAMESTRING;
case UIDevice1GiPad : return IPAD_1G_NAMESTRING;
case UIDevice2GiPad : return IPAD_2G_NAMESTRING;
case UIDevice3GiPad : return IPAD_3G_NAMESTRING;
case UIDevice4GiPad : return IPAD_4G_NAMESTRING;
case UIDevice5GiPad : return IPAD_5G_NAMESTRING;
case UIDevice6GiPad : return IPAD_6G_NAMESTRING;
case UIDeviceiPadPro12_93g : return IPAD_PRO_12_9_NAMESTRING;
case UIDeviceiPadPro11 : return IPAD_PRO_11_NAMESTRING;
case UIDeviceiPadPro6g : return IPAD_PRO_6G_NAMESTRING;
case UIDeviceiPadPro10_5 : return IPAD_PRO_10_5_NAMESTRING;
case UIDeviceiPadPro12_9 : return IPAD_PRO_12_9_NAMESTRING;
case UIDeviceUnknowniPad : return IPAD_UNKNOWN_NAMESTRING;
case UIDeviceAppleTV2 : return APPLETV_2G_NAMESTRING;
case UIDeviceAppleTV3 : return APPLETV_3G_NAMESTRING;
case UIDeviceAppleTV4 : return APPLETV_4G_NAMESTRING;
case UIDeviceUnknownAppleTV: return APPLETV_UNKNOWN_NAMESTRING;
case UIDeviceSimulator: return SIMULATOR_NAMESTRING;
case UIDeviceSimulatoriPhone: return SIMULATOR_IPHONE_NAMESTRING;
case UIDeviceSimulatoriPad: return SIMULATOR_IPAD_NAMESTRING;
case UIDeviceSimulatorAppleTV: return SIMULATOR_APPLETV_NAMESTRING;
case UIDeviceIFPGA: return IFPGA_NAMESTRING;
case UIDeviceOSX: return [self macHWName];
default: return IOS_FAMILY_UNKNOWN_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";
}
- (NSUInteger)platformType
{
#if TARGET_OS_IPHONE
NSString *platform = [self platform];
// The ever mysterious iFPGA
if ([platform isEqualToString:@"iFPGA"]) return UIDeviceIFPGA;
// iPhone
if ([platform isEqualToString:@"iPhone1,1"]) return UIDevice1GiPhone;
if ([platform isEqualToString:@"iPhone1,2"]) return UIDevice3GiPhone;
if ([platform hasPrefix:@"iPhone2"]) return UIDevice3GSiPhone;
if ([platform hasPrefix:@"iPhone3"]) return UIDevice4iPhone;
if ([platform hasPrefix:@"iPhone4"]) return UIDevice4SiPhone;
if ([platform hasPrefix:@"iPhone5"]) return UIDevice5iPhone;
if ([platform hasPrefix:@"iPhone6"]) return UIDevice5SiPhone;
if ([platform isEqualToString:@"iPhone7,1"]) return UIDevice6PlusiPhone;
if ([platform isEqualToString:@"iPhone7,2"]) return UIDevice6iPhone;
if ([platform isEqualToString:@"iPhone8,1"]) return UIDevice6siPhone;
if ([platform isEqualToString:@"iPhone8,2"]) return UIDevice6SPlusiPhone;
if ([platform isEqualToString:@"iPhone9,1"]) return UIDevice7iPhone;
if ([platform isEqualToString:@"iPhone9,3"]) return UIDevice7iPhone;
if ([platform isEqualToString:@"iPhone9,2"]) return UIDevice7PlusiPhone;
if ([platform isEqualToString:@"iPhone9,4"]) return UIDevice7PlusiPhone;
if ([platform isEqualToString:@"iPhone10,1"]) return UIDevice8iPhone;
if ([platform isEqualToString:@"iPhone10,4"]) return UIDevice8iPhone;
if ([platform isEqualToString:@"iPhone10,2"]) return UIDevice8PlusiPhone;
if ([platform isEqualToString:@"iPhone10,5"]) return UIDevice8PlusiPhone;
if ([platform isEqualToString:@"iPhone10,3"]) return UIDeviceXiPhone;
if ([platform isEqualToString:@"iPhone10,6"]) return UIDeviceXiPhone;
if ([platform isEqualToString:@"iPhone11,2"]) return UIDeviceXSiPhone;
if ([platform isEqualToString:@"iPhone11,6"]) return UIDeviceXSMaxiPhone;
if ([platform isEqualToString:@"iPhone11,4"]) return UIDeviceXSMaxiPhone;
if ([platform isEqualToString:@"iPhone11,8"]) return UIDeviceXRiPhone;
if ([platform isEqualToString:@"iPhone8,4"]) return UIDeviceSEPhone;
// iPod
if ([platform hasPrefix:@"iPod1"]) return UIDevice1GiPod;
if ([platform hasPrefix:@"iPod2"]) return UIDevice2GiPod;
if ([platform hasPrefix:@"iPod3"]) return UIDevice3GiPod;
if ([platform hasPrefix:@"iPod4"]) return UIDevice4GiPod;
if ([platform hasPrefix:@"iPod5"]) return UIDevice5GiPod;
if ([platform hasPrefix:@"iPod7"]) return UIDevice6GiPod;
// iPad
if ([platform hasPrefix:@"iPad1"]) return UIDevice1GiPad;
if ([platform hasPrefix:@"iPad2"]) return UIDevice2GiPad;
if ([platform hasPrefix:@"iPad3"]) return UIDevice3GiPad;
if ([platform hasPrefix:@"iPad4"]) return UIDevice4GiPad;
if ([platform hasPrefix:@"iPad5"]) return UIDevice5GiPad;
if ([platform hasPrefix:@"iPad6"]) return UIDevice6GiPad;
if ([platform isEqualToString:@"iPad8,5"] ||
[platform isEqualToString:@"iPad8,6"] ||
[platform isEqualToString:@"iPad8,7"] ||
[platform isEqualToString:@"iPad8,8"]) {
return UIDeviceiPadPro12_93g;
}
if ([platform isEqualToString:@"iPad8,1"] ||
[platform isEqualToString:@"iPad8,2"] ||
[platform isEqualToString:@"iPad8,3"] ||
[platform isEqualToString:@"iPad8,4"]) {
return UIDeviceiPadPro11;
}
if ([platform isEqualToString:@"iPad7,5"] ||
[platform isEqualToString:@"iPad7,6"]) {
return UIDeviceiPadPro6g;
}
if ([platform isEqualToString:@"iPad7,3"] ||
[platform isEqualToString:@"iPad7,4"]) {
return UIDeviceiPadPro10_5;
}
if ([platform isEqualToString:@"iPad7,1"] ||
[platform isEqualToString:@"iPad7,2"]) {
return UIDeviceiPadPro12_9;
}
// Apple TV
if ([platform hasPrefix:@"AppleTV2"]) return UIDeviceAppleTV2;
if ([platform hasPrefix:@"AppleTV3"]) return UIDeviceAppleTV3;
if ([platform hasPrefix:@"iPhone"]) return UIDeviceUnknowniPhone;
if ([platform hasPrefix:@"iPod"]) return UIDeviceUnknowniPod;
if ([platform hasPrefix:@"iPad"]) return UIDeviceUnknowniPad;
if ([platform hasPrefix:@"AppleTV"]) return UIDeviceUnknownAppleTV;
#define IPAD_PRO_3G_NAMESTRING @"iPad Pro 12.9 (3rd gen)"
#define IPAD_PRO_11_NAMESTRING @"iPad Pro 11"
#define IPAD_PRO_6G_NAMESTRING @"iPad (6th gen)"
#define IPAD_PRO_10_5_NAMESTRING @"iPad Pro 10.5"
#define IPAD_PRO_12_9_NAMESTRING @"iPad Pro 12.9"
// Simulator thanks Jordan Breeding
if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"])
{
return UIDeviceSimulatoriPhone;
}
#else
return UIDeviceOSX;
#endif
return UIDeviceUnknown;
}
- (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] init];
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->_socksProxySettings = self.socksProxySettings;
result->_networkSettings = self.networkSettings;
result->_systemCode = self.systemCode;
[result _updateApiInitializationHash];
return result;
}
- (instancetype)copyWithZone:(NSZone *)__unused zone {
MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
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 _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings {
MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
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 _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings {
MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
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 _updateApiInitializationHash];
return result;
}
- (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode {
MTApiEnvironment *result = [[MTApiEnvironment alloc] init];
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 _updateApiInitializationHash];
return result;
}
@end

View File

@ -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

View File

@ -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

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

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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,115 @@
#import <Foundation/Foundation.h>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTDatacenterAuthInfo.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTDatacenterAuthInfo.h>
#else
# import <MtProtoKit/MTDatacenterAuthInfo.h>
#endif
@class MTDatacenterAddress;
@class MTDatacenterAddressSet;
@protocol MTSerialization;
@class MTContext;
@class MTTransportScheme;
@protocol MTKeychain;
@class MTSessionInfo;
@class MTApiEnvironment;
@class MTSignal;
@protocol MTContextChangeListener <NSObject>
@optional
- (void)contextDatacenterAddressSetUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet;
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;
- (void)contextDatacenterAuthTokenUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authToken:(id)authToken;
- (void)contextDatacenterTransportSchemesUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextIsPasswordRequiredUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextDatacenterPublicKeysUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
- (MTSignal *)fetchContextDatacenterPublicKeys:(MTContext *)context datacenterId:(NSInteger)datacenterId;
- (void)contextApiEnvironmentUpdated:(MTContext *)context apiEnvironment:(MTApiEnvironment *)apiEnvironment;
- (MTSignal *)isContextNetworkAccessAllowed:(MTContext *)context;
@end
@interface MTContextBlockChangeListener : NSObject <MTContextChangeListener>
@property (nonatomic, copy) void (^contextIsPasswordRequiredUpdated)(MTContext *, NSInteger);
@property (nonatomic, copy) MTSignal *(^fetchContextDatacenterPublicKeys)(MTContext *, NSInteger);
@property (nonatomic, copy) MTSignal *(^isContextNetworkAccessAllowed)(MTContext *);
@end
@interface MTContext : NSObject
@property (nonatomic, strong) id<MTKeychain> keychain;
@property (nonatomic, strong, readonly) id<MTSerialization> serialization;
@property (nonatomic, strong, readonly) MTApiEnvironment *apiEnvironment;
@property (nonatomic, readonly) bool isTestingEnvironment;
@property (nonatomic, readonly) bool useTempAuthKeys;
- (instancetype)initWithSerialization:(id<MTSerialization>)serialization apiEnvironment:(MTApiEnvironment *)apiEnvironment isTestingEnvironment:(bool)isTestingEnvironment useTempAuthKeys:(bool)useTempAuthKeys;
- (void)performBatchUpdates:(void (^)())block;
- (void)addChangeListener:(id<MTContextChangeListener>)changeListener;
- (void)removeChangeListener:(id<MTContextChangeListener>)changeListener;
- (void)setDiscoverBackupAddressListSignal:(MTSignal *)signal;
- (NSTimeInterval)globalTime;
- (NSTimeInterval)globalTimeDifference;
- (NSTimeInterval)globalTimeOffsetFromUTC;
- (void)setGlobalTimeDifference:(NSTimeInterval)globalTimeDifference;
- (void)setSeedAddressSetForDatacenterWithId:(NSInteger)datacenterId seedAddressSet:(MTDatacenterAddressSet *)seedAddressSet;
- (void)updateAddressSetForDatacenterWithId:(NSInteger)datacenterId addressSet:(MTDatacenterAddressSet *)addressSet forceUpdateSchemes:(bool)forceUpdateSchemes;
- (void)addAddressForDatacenterWithId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
- (void)updateTransportSchemeForDatacenterWithId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media isProxy:(bool)isProxy;
- (void)updateAuthInfoForDatacenterWithId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)authInfo;
- (bool)isPasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId;
- (bool)updatePasswordInputRequiredForDatacenterWithId:(NSInteger)datacenterId required:(bool)required;
- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo;
- (void)collectSessionIdsForCleanupWithAuthKeyId:(int64_t)authKeyId completion:(void (^)(NSArray *sessionIds))completion;
- (void)sessionIdsDeletedForAuthKeyId:(int64_t)authKeyId sessionIds:(NSArray *)sessionIds;
- (NSArray *)knownDatacenterIds;
- (void)enumerateAddressSetsForDatacenters:(void (^)(NSInteger datacenterId, MTDatacenterAddressSet *addressSet, BOOL *stop))block;
- (MTDatacenterAddressSet *)addressSetForDatacenterWithId:(NSInteger)datacenterId;
- (void)reportTransportSchemeFailureForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme;
- (void)reportTransportSchemeSuccessForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme;
- (void)invalidateTransportSchemesForDatacenterIds:(NSArray<NSNumber *> * _Nonnull)datacenterIds;
- (void)invalidateTransportSchemesForKnownDatacenterIds;
- (MTTransportScheme * _Nullable)chooseTransportSchemeForConnectionToDatacenterId:(NSInteger)datacenterId schemes:(NSArray<MTTransportScheme *> * _Nonnull)schemes;
- (NSArray<MTTransportScheme *> * _Nonnull)transportSchemesForDatacenterWithId:(NSInteger)datacenterId media:(bool)media enforceMedia:(bool)enforceMedia isProxy:(bool)isProxy;
- (void)transportSchemeForDatacenterWithIdRequired:(NSInteger)datacenterId media:(bool)media;
- (void)invalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme isProbablyHttp:(bool)isProbablyHttp media:(bool)media;
- (void)revalidateTransportSchemeForDatacenterId:(NSInteger)datacenterId transportScheme:(MTTransportScheme *)transportScheme media:(bool)media;
- (MTDatacenterAuthInfo *)authInfoForDatacenterWithId:(NSInteger)datacenterId;
- (NSArray<NSDictionary *> *)publicKeysForDatacenterWithId:(NSInteger)datacenterId;
- (void)updatePublicKeysForDatacenterWithId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
- (void)publicKeysForDatacenterWithIdRequired:(NSInteger)datacenterId;
- (void)removeAllAuthTokens;
- (id)authTokenForDatacenterWithId:(NSInteger)datacenterId;
- (void)updateAuthTokenForDatacenterWithId:(NSInteger)datacenterId authToken:(id)authToken;
- (void)addressSetForDatacenterWithIdRequired:(NSInteger)datacenterId;
- (void)authInfoForDatacenterWithIdRequired:(NSInteger)datacenterId isCdn:(bool)isCdn;
- (void)tempAuthKeyForDatacenterWithIdRequired:(NSInteger)datacenterId keyType:(MTDatacenterAuthTempKeyType)keyType;
- (void)authTokenForDatacenterWithIdRequired:(NSInteger)datacenterId authToken:(id)authToken masterDatacenterId:(NSInteger)masterDatacenterId;
- (void)reportProblemsWithDatacenterAddressForId:(NSInteger)datacenterId address:(MTDatacenterAddress *)address;
- (void)updateApiEnvironment:(MTApiEnvironment *(^)(MTApiEnvironment *))f;
- (void)beginExplicitBackupAddressDiscovery;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
#import <Foundation/Foundation.h>
@interface MTDatacenterAddress : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong, readonly) NSString *host;
@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) uint16_t port;
@property (nonatomic, readonly) bool preferForMedia;
@property (nonatomic, readonly) bool restrictToTcp;
@property (nonatomic, readonly) bool cdn;
@property (nonatomic, readonly) bool preferForProxy;
@property (nonatomic, readonly) NSData *secret;
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port preferForMedia:(bool)preferForMedia restrictToTcp:(bool)restrictToTcp cdn:(bool)cdn preferForProxy:(bool)preferForProxy secret:(NSData *)secret;
- (BOOL)isEqualToAddress:(MTDatacenterAddress *)other;
- (BOOL)isIpv6;
@end

View File

@ -0,0 +1,124 @@
#import "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

View File

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTDatacenterAddressListData : NSObject
@property (nonatomic, strong, readonly) NSDictionary<NSNumber *, NSArray *> *addressList;
- (instancetype)initWithAddressList:(NSDictionary<NSNumber *, NSArray *> *)addressList;
@end

View File

@ -0,0 +1,19 @@
#import "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

View File

@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
@class MTDatacenterAddress;
@interface MTDatacenterAddressSet : NSObject <NSCoding>
@property (nonatomic, strong, readonly) NSArray *addressList;
- (instancetype)initWithAddressList:(NSArray *)addressList;
- (MTDatacenterAddress *)firstAddress;
@end

View File

@ -0,0 +1,71 @@
#import "MTDatacenterAddressSet.h"
#import "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

View File

@ -0,0 +1,30 @@
#import <Foundation/Foundation.h>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTDatacenterAuthInfo.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTDatacenterAuthInfo.h>
#else
# import <MtProtoKit/MTDatacenterAuthInfo.h>
#endif
@class MTContext;
@class MTDatacenterAuthAction;
@protocol MTDatacenterAuthActionDelegate <NSObject>
- (void)datacenterAuthActionCompleted:(MTDatacenterAuthAction *)action;
@end
@interface MTDatacenterAuthAction : NSObject
@property (nonatomic, readonly) bool tempAuth;
@property (nonatomic, weak) id<MTDatacenterAuthActionDelegate> delegate;
- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType;
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn;
- (void)cancel;
@end

View File

@ -0,0 +1,165 @@
#import "MTDatacenterAuthAction.h"
#import "MTLogging.h"
#import "MTContext.h"
#import "MTProto.h"
#import "MTRequest.h"
#import "MTDatacenterSaltInfo.h"
#import "MTDatacenterAuthInfo.h"
#import "MTApiEnvironment.h"
#import "MTSerialization.h"
#import "MTDatacenterAddressSet.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTSignal.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTSignal.h>
#else
# import <MtProtoKit/MTSignal.h>
#endif
#import "MTDatacenterAuthMessageService.h"
#import "MTRequestMessageService.h"
#import "MTBuffer.h"
@interface MTDatacenterAuthAction () <MTDatacenterAuthMessageServiceDelegate>
{
bool _isCdn;
MTDatacenterAuthTempKeyType _tempAuthKeyType;
NSInteger _datacenterId;
__weak MTContext *_context;
bool _awaitingAddresSetUpdate;
MTProto *_authMtProto;
MTMetaDisposable *_verifyDisposable;
}
@end
@implementation MTDatacenterAuthAction
- (instancetype)initWithTempAuth:(bool)tempAuth tempAuthKeyType:(MTDatacenterAuthTempKeyType)tempAuthKeyType {
self = [super init];
if (self != nil) {
_tempAuth = tempAuth;
_tempAuthKeyType = tempAuthKeyType;
_verifyDisposable = [[MTMetaDisposable alloc] init];
}
return self;
}
- (void)dealloc
{
[self cleanup];
}
- (void)execute:(MTContext *)context datacenterId:(NSInteger)datacenterId isCdn:(bool)isCdn
{
_datacenterId = datacenterId;
_context = context;
_isCdn = isCdn;
if (_datacenterId != 0 && context != nil)
{
bool alreadyCompleted = false;
MTDatacenterAuthInfo *currentAuthInfo = [context authInfoForDatacenterWithId:_datacenterId];
if (currentAuthInfo != nil) {
if (_tempAuth) {
if ([currentAuthInfo tempAuthKeyWithType:_tempAuthKeyType] != nil) {
alreadyCompleted = true;
}
} else {
alreadyCompleted = true;
}
}
if (alreadyCompleted) {
[self complete];
} else {
_authMtProto = [[MTProto alloc] initWithContext:context datacenterId:_datacenterId usageCalculationInfo:nil];
_authMtProto.cdn = isCdn;
_authMtProto.useUnauthorizedMode = true;
if (_tempAuth) {
switch (_tempAuthKeyType) {
case MTDatacenterAuthTempKeyTypeMain:
_authMtProto.media = false;
break;
case MTDatacenterAuthTempKeyTypeMedia:
_authMtProto.media = true;
_authMtProto.enforceMedia = true;
break;
default:
break;
}
}
MTDatacenterAuthMessageService *authService = [[MTDatacenterAuthMessageService alloc] initWithContext:context tempAuth:_tempAuth];
authService.delegate = self;
[_authMtProto addMessageService:authService];
}
}
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 (_tempAuth) {
MTContext *mainContext = _context;
if (mainContext != nil) {
MTContext *context = _context;
[context performBatchUpdates:^{
MTDatacenterAuthInfo *authInfo = [context authInfoForDatacenterWithId:_datacenterId];
if (authInfo != nil) {
authInfo = [authInfo withUpdatedTempAuthKeyWithType:_tempAuthKeyType key:authKey];
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
}
}];
[self complete];
}
} else {
MTDatacenterAuthInfo *authInfo = [[MTDatacenterAuthInfo alloc] initWithAuthKey:authKey.authKey authKeyId:authKey.authKeyId saltSet:@[[[MTDatacenterSaltInfo alloc] initWithSalt:0 firstValidMessageId:timestamp lastValidMessageId:timestamp + (29.0 * 60.0) * 4294967296]] authKeyAttributes:nil mainTempAuthKey:nil mediaTempAuthKey:nil];
MTContext *context = _context;
[context updateAuthInfoForDatacenterWithId:_datacenterId authInfo:authInfo];
[self complete];
}
}
- (void)cleanup
{
MTProto *authMtProto = _authMtProto;
_authMtProto = nil;
[authMtProto stop];
[_verifyDisposable dispose];
}
- (void)cancel
{
[self cleanup];
[self fail];
}
- (void)complete
{
id<MTDatacenterAuthActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
[delegate datacenterAuthActionCompleted:self];
}
- (void)fail
{
id<MTDatacenterAuthActionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(datacenterAuthActionCompleted:)])
[delegate datacenterAuthActionCompleted:self];
}
@end

View File

@ -0,0 +1,38 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, MTDatacenterAuthTempKeyType) {
MTDatacenterAuthTempKeyTypeMain,
MTDatacenterAuthTempKeyTypeMedia
};
@interface MTDatacenterAuthKey: NSObject <NSCoding>
@property (nonatomic, strong, readonly) NSData *authKey;
@property (nonatomic, readonly) int64_t authKeyId;
@property (nonatomic, readonly) bool notBound;
- (instancetype)initWithAuthKey:(NSData *)tempAuthKey authKeyId:(int64_t)authKeyId notBound:(bool)notBound;
@end
@interface MTDatacenterAuthInfo : NSObject <NSCoding>
@property (nonatomic, strong, readonly) NSData *authKey;
@property (nonatomic, readonly) int64_t authKeyId;
@property (nonatomic, strong, readonly) NSArray *saltSet;
@property (nonatomic, strong, readonly) NSDictionary *authKeyAttributes;
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mainTempAuthKey;
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *mediaTempAuthKey;
@property (nonatomic, strong, readonly) MTDatacenterAuthKey *persistentAuthKey;
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey;
- (int64_t)authSaltForMessageId:(int64_t)messageId;
- (MTDatacenterAuthInfo *)mergeSaltSet:(NSArray *)updatedSaltSet forTimestamp:(NSTimeInterval)timestamp;
- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes;
- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type;
- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key;
@end

View File

@ -0,0 +1,155 @@
#import "MTDatacenterAuthInfo.h"
#import "MTDatacenterSaltInfo.h"
@implementation MTDatacenterAuthKey
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId notBound:(bool)notBound {
self = [super init];
if (self != nil) {
_authKey = authKey;
_authKeyId = authKeyId;
_notBound = notBound;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
return [self initWithAuthKey:[aDecoder decodeObjectForKey:@"key"] authKeyId:[aDecoder decodeInt64ForKey:@"keyId"] notBound:[aDecoder decodeBoolForKey:@"notBound"]];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_authKey forKey:@"key"];
[aCoder encodeInt64:_authKeyId forKey:@"keyId"];
[aCoder encodeBool:_notBound forKey:@"notBound"];
}
@end
@implementation MTDatacenterAuthInfo
- (instancetype)initWithAuthKey:(NSData *)authKey authKeyId:(int64_t)authKeyId saltSet:(NSArray *)saltSet authKeyAttributes:(NSDictionary *)authKeyAttributes mainTempAuthKey:(MTDatacenterAuthKey *)mainTempAuthKey mediaTempAuthKey:(MTDatacenterAuthKey *)mediaTempAuthKey
{
self = [super init];
if (self != nil)
{
_authKey = authKey;
_authKeyId = authKeyId;
_saltSet = saltSet;
_authKeyAttributes = authKeyAttributes;
_mainTempAuthKey = mainTempAuthKey;
_mediaTempAuthKey = mediaTempAuthKey;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_authKey = [aDecoder decodeObjectForKey:@"authKey"];
_authKeyId = [aDecoder decodeInt64ForKey:@"authKeyId"];
_saltSet = [aDecoder decodeObjectForKey:@"saltSet"];
_authKeyAttributes = [aDecoder decodeObjectForKey:@"authKeyAttributes"];
_mainTempAuthKey = [aDecoder decodeObjectForKey:@"tempAuthKey"];
_mediaTempAuthKey = [aDecoder decodeObjectForKey:@"mediaTempAuthKey"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_authKey forKey:@"authKey"];
[aCoder encodeInt64:_authKeyId forKey:@"authKeyId"];
[aCoder encodeObject:_saltSet forKey:@"saltSet"];
[aCoder encodeObject:_authKeyAttributes forKey:@"authKeyAttributes"];
[aCoder encodeObject:_mainTempAuthKey forKey:@"tempAuthKey"];
[aCoder encodeObject:_mediaTempAuthKey forKey:@"mediaTempAuthKey"];
}
- (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 saltSet:mergedSaltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
}
- (MTDatacenterAuthInfo *)withUpdatedAuthKeyAttributes:(NSDictionary *)authKeyAttributes {
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:_mediaTempAuthKey];
}
- (MTDatacenterAuthKey *)tempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type {
switch (type) {
case MTDatacenterAuthTempKeyTypeMain:
return _mainTempAuthKey;
case MTDatacenterAuthTempKeyTypeMedia:
return _mediaTempAuthKey;
default:
NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
return nil;
}
}
- (MTDatacenterAuthInfo *)withUpdatedTempAuthKeyWithType:(MTDatacenterAuthTempKeyType)type key:(MTDatacenterAuthKey *)key {
switch (type) {
case MTDatacenterAuthTempKeyTypeMain:
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:key mediaTempAuthKey:_mediaTempAuthKey];
case MTDatacenterAuthTempKeyTypeMedia:
return [[MTDatacenterAuthInfo alloc] initWithAuthKey:_authKey authKeyId:_authKeyId saltSet:_saltSet authKeyAttributes:_authKeyAttributes mainTempAuthKey:_mainTempAuthKey mediaTempAuthKey:key];
default:
NSAssert(false, @"unknown MTDatacenterAuthTempKeyType");
return self;
}
}
- (MTDatacenterAuthKey *)persistentAuthKey {
return [[MTDatacenterAuthKey alloc] initWithAuthKey:_authKey authKeyId:_authKeyId notBound:false];
}
- (NSString *)description {
return [NSString stringWithFormat:@"MTDatacenterAuthInfo authKeyId:%" PRId64 " authKey:%lu", _authKeyId, (unsigned long)_authKey.length];
}
@end

View File

@ -0,0 +1,27 @@
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTMessageService.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTMessageService.h>
#else
# import <MtProtoKit/MTMessageService.h>
#endif
@class MTContext;
@class MTDatacenterAuthMessageService;
@class MTDatacenterAuthKey;
@protocol MTDatacenterAuthMessageServiceDelegate <NSObject>
- (void)authMessageServiceCompletedWithAuthKey:(MTDatacenterAuthKey *)authKey timestamp:(int64_t)timestamp;
@end
@interface MTDatacenterAuthMessageService : NSObject <MTMessageService>
@property (nonatomic, weak) id<MTDatacenterAuthMessageServiceDelegate> delegate;
- (instancetype)initWithContext:(MTContext *)context tempAuth:(bool)tempAuth;
@end

View File

@ -0,0 +1,789 @@
#import "MTDatacenterAuthMessageService.h"
#import "MTLogging.h"
#import "MTContext.h"
#import "MTProto.h"
#import "MTSerialization.h"
#import "MTSessionInfo.h"
#import "MTIncomingMessage.h"
#import "MTOutgoingMessage.h"
#import "MTMessageTransaction.h"
#import "MTPreparedMessage.h"
#import "MTDatacenterAuthInfo.h"
#import "MTDatacenterSaltInfo.h"
#import "MTBuffer.h"
#import "MTEncryption.h"
#import "MTInternalMessageParser.h"
#import "MTServerDhInnerDataMessage.h"
#import "MTResPqMessage.h"
#import "MTServerDhParamsMessage.h"
#import "MTSetClientDhParamsResponseMessage.h"
static NSArray *defaultPublicKeys() {
static NSArray *serverPublicKeys = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
serverPublicKeys = [[NSArray alloc] initWithObjects:
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\n"
"ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\n"
"vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\n"
"xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\n"
"XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\n"
"NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x9a996a1db11c729bUL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\n"
"DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n"
"1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\n"
"g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\n"
"hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\n"
"x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xb05b2a6f70cdea78UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\n"
"lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\n"
"an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\n"
"Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n"
"8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\n"
"Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xc3b42b026ce86b21UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\n"
"xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\n"
"qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n"
"/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\n"
"WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\n"
"UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x71e025b6c76033e3UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX\n"
"riwt4rROd9csv0t0OHCaTmRqBcQ0J8fxhN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/\n"
"j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvdl84Kd9ORYjDEAyFnEA7dD556OptgLQQ2\n"
"e2iVNq8NZLYTzLp5YpOdO1doK+ttrltggTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnS\n"
"Lj16yE5HvJQn0CNpRdENvRUXe6tBP78O39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wF\n"
"XGF710w9lwCGNbmNxNYhtIkdqfsEcwR5JwIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xbc35f3509f7b7a5UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAvfLHfYH2r9R70w8prHblWt/nDkh+XkgpflqQVcnAfSuTtO05lNPs\n"
"pQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOOKPi0OfJXoRVylFzAQG/j83u5K3kRLbae\n"
"7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ3TDS2pQOCtovG4eDl9wacrXOJTG2990V\n"
"jgnIKNA0UMoP+KF03qzryqIt3oTvZq03DyWdGK+AZjgBLaDKSnC6qD2cFY81UryR\n"
"WOab8zKkWAnhw2kFpcqhI0jdV5QaSCExvnsjVaX0Y1N0870931/5Jb9ICe4nweZ9\n"
"kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV/wIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x15ae5fa8b5529542UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAs/ditzm+mPND6xkhzwFIz6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGr\n"
"zqTDHkO30R8VeRM/Kz2f4nR05GIFiITl4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+\n"
"th6knSU0yLtNKuQVP6voMrnt9MV1X92LGZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvS\n"
"Uwwc+yi1/gGaybwlzZwqXYoPOhwMebzKUk0xW14htcJrRrq+PXXQbRzTMynseCoP\n"
"Ioke0dtCodbA3qQxQovE16q9zz4Otv2k4j63cz53J+mhkVWAeWxVGI0lltJmWtEY\n"
"K6er8VqqWot3nqmWMXogrgRLggv/NbbooQIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0xaeae98e13cd7f94fUL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q05shjg8/4p6047bn6/m8yPy1RBsvIyvuD\n"
"uGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xbnfxL5BXHplJhMtADXKM9bWB11PU1Eioc\n"
"3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvi\n"
"fRLJbY08/Gp66KpQvy7g8w7VB8wlgePexW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqe\n"
"Pji9NP3tJUFQjcECqcm0yV7/2d0t/pbCm+ZH1sadZspQCEPPrtbkQBlvHb4OLiIW\n"
"PGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6MAQIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x5a181b2235057d98UL], @"fingerprint", nil],
[[NSDictionary alloc] initWithObjectsAndKeys:@"-----BEGIN RSA PUBLIC KEY-----\n"
"MIIBCgKCAQEAr4v4wxMDXIaMOh8bayF/NyoYdpcysn5EbjTIOZC0RkgzsRj3SGlu\n"
"52QSz+ysO41dQAjpFLgxPVJoOlxXokaOq827IfW0bGCm0doT5hxtedu9UCQKbE8j\n"
"lDOk+kWMXHPZFJKWRgKgTu9hcB3y3Vk+JFfLpq3d5ZB48B4bcwrRQnzkx5GhWOFX\n"
"x73ZgjO93eoQ2b/lDyXxK4B4IS+hZhjzezPZTI5upTRbs5ljlApsddsHrKk6jJNj\n"
"8Ygs/ps8e6ct82jLXbnndC9s8HjEvDvBPH9IPjv5JUlmHMBFZ5vFQIfbpo0u0+1P\n"
"n6bkEi5o7/ifoyVv2pAZTRwppTz0EuXD8QIDAQAB\n"
"-----END RSA PUBLIC KEY-----", @"key", [[NSNumber alloc] initWithUnsignedLongLong:0x9692106da14b9f02UL], @"fingerprint", nil],
nil];
});
return serverPublicKeys;
}
static NSDictionary *selectPublicKey(NSArray *fingerprints, NSArray<NSDictionary *> *publicKeys)
{
for (NSNumber *nFingerprint in fingerprints)
{
for (NSDictionary *keyDesc in publicKeys)
{
int64_t keyFingerprint = [[keyDesc objectForKey:@"fingerprint"] longLongValue];
if ([nFingerprint longLongValue] == keyFingerprint)
return keyDesc;
}
}
return nil;
}
typedef enum {
MTDatacenterAuthStageWaitingForPublicKeys = 0,
MTDatacenterAuthStagePQ = 1,
MTDatacenterAuthStageReqDH = 2,
MTDatacenterAuthStageKeyVerification = 3,
MTDatacenterAuthStageDone = 4
} MTDatacenterAuthStage;
@interface MTDatacenterAuthMessageService ()
{
bool _tempAuth;
MTSessionInfo *_sessionInfo;
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<NSDictionary *> *_publicKeys;
}
@end
@implementation MTDatacenterAuthMessageService
- (instancetype)initWithContext:(MTContext *)context tempAuth:(bool)tempAuth
{
self = [super init];
if (self != nil)
{
_tempAuth = tempAuth;
_sessionInfo = [[MTSessionInfo alloc] initWithRandomSessionIdAndContext:context];
}
return self;
}
- (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 = [mtProto.context publicKeysForDatacenterWithId:mtProto.datacenterId];
if (_publicKeys == nil) {
_stage = MTDatacenterAuthStageWaitingForPublicKeys;
[mtProto.context publicKeysForDatacenterWithIdRequired:mtProto.datacenterId];
} else {
_stage = MTDatacenterAuthStagePQ;
}
} else {
_publicKeys = defaultPublicKeys();
_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 (_stage == MTDatacenterAuthStageWaitingForPublicKeys) {
if (mtProto.datacenterId == datacenterId) {
_publicKeys = publicKeys;
if (_publicKeys != nil && _publicKeys.count != 0) {
_stage = MTDatacenterAuthStagePQ;
[mtProto requestTransportTransaction];
}
}
}
}
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto
{
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)0x60469778];
[reqPqBuffer appendBytes:_nonce.bytes length:_nonce.length];
NSString *messageDescription = [NSString stringWithFormat:@"reqPq nonce:%@", _nonce];
MTOutgoingMessage *message = [[MTOutgoingMessage alloc] initWithData:reqPqBuffer.data metadata:messageDescription 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:
{
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", _nonce, _serverNonce, _dhP, _dhQ, _dhPublicKeyFingerprint];
MTOutgoingMessage *message = [[MTOutgoingMessage alloc] initWithData:reqDhBuffer.data metadata:messageDescription 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" 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;
}
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message
{
if (_stage == MTDatacenterAuthStagePQ && [message.body isKindOfClass:[MTResPqMessage class]])
{
MTResPqMessage *resPqMessage = message.body;
if ([_nonce isEqualToData:resPqMessage.nonce])
{
NSDictionary *publicKey = selectPublicKey(resPqMessage.serverPublicKeyFingerprints, _publicKeys);
if (publicKey == nil && mtProto.cdn && resPqMessage.serverPublicKeyFingerprints.count == 1 && _publicKeys.count == 1) {
publicKey = @{@"key": _publicKeys[0][@"key"], @"fingerprint": resPqMessage.serverPublicKeyFingerprints[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 objectForKey:@"fingerprint"] longLongValue];
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:60 * 60 * 32];
NSData *innerDataBytes = innerDataBuffer.data;
NSMutableData *dataWithHash = [[NSMutableData alloc] init];
[dataWithHash appendData:MTSha1(innerDataBytes)];
[dataWithHash appendData:innerDataBytes];
while (dataWithHash.length < 255)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[dataWithHash appendBytes:&random length:1];
}
NSData *encryptedData = MTRsaEncrypt([publicKey objectForKey:@"key"], dataWithHash);
if (encryptedData.length < 256)
{
NSMutableData *newEncryptedData = [[NSMutableData alloc] init];
for (int i = 0; i < 256 - (int)encryptedData.length; i++)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[newEncryptedData appendBytes:&random length:1];
}
[newEncryptedData appendData:encryptedData];
encryptedData = newEncryptedData;
}
_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;
NSMutableData *dataWithHash = [[NSMutableData alloc] init];
[dataWithHash appendData:MTSha1(innerDataBytes)];
[dataWithHash appendData:innerDataBytes];
while (dataWithHash.length < 255)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[dataWithHash appendBytes:&random length:1];
}
NSData *encryptedData = MTRsaEncrypt([publicKey objectForKey:@"key"], dataWithHash);
if (encryptedData.length < 256)
{
NSMutableData *newEncryptedData = [[NSMutableData alloc] init];
for (int i = 0; i < 256 - (int)encryptedData.length; i++)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[newEncryptedData appendBytes:&random length:1];
}
[newEncryptedData appendData:encryptedData];
encryptedData = newEncryptedData;
}
_dhEncryptedData = encryptedData;
}
_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(innerDataGA, innerDataDhPrime))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g_a]", self);
}
[self reset:mtProto];
return;
}
if (!MTCheckMod(innerDataDhPrime, (unsigned int)innerDataG, mtProto.context.keychain))
{
if (MTLogEnabled()) {
MTLog(@"[MTDatacenterAuthMessageService#%p invalid DH g (2)]", self);
}
[self reset:mtProto];
return;
}
if (!MTCheckIsSafePrime(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(g, b, innerDataDhPrime);
NSData *authKey = MTExp(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];
}
_authKey = [[MTDatacenterAuthKey alloc] initWithAuthKey:authKey authKeyId:authKeyId 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

View File

@ -0,0 +1,16 @@
#import <Foundation/Foundation.h>
@interface MTDatacenterSaltInfo : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t salt;
@property (nonatomic, readonly) int64_t firstValidMessageId;
@property (nonatomic, readonly) int64_t lastValidMessageId;
- (instancetype)initWithSalt:(int64_t)salt firstValidMessageId:(int64_t)firstValidMessageId lastValidMessageId:(int64_t)lastValidMessageId;
- (int64_t)validMessageCountAfterId:(int64_t)messageId;
- (bool)isValidFutureSaltForMessageId:(int64_t)messageId;
@end

View File

@ -0,0 +1,51 @@
#import "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

View File

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
@class MTContext;
@class MTDatacenterTransferAuthAction;
@protocol MTDatacenterTransferAuthActionDelegate <NSObject>
- (void)datacenterTransferAuthActionCompleted:(MTDatacenterTransferAuthAction *)action;
@end
@interface MTDatacenterTransferAuthAction : NSObject
@property (nonatomic, weak) id<MTDatacenterTransferAuthActionDelegate> delegate;
- (void)execute:(MTContext *)context masterDatacenterId:(NSInteger)masterDatacenterId destinationDatacenterId:(NSInteger)destinationDatacenterId authToken:(id)authToken;
- (void)cancel;
@end

View File

@ -0,0 +1,185 @@
#import "MTDatacenterTransferAuthAction.h"
#import "MTContext.h"
#import "MTSerialization.h"
#import "MTProto.h"
#import "MTRequestMessageService.h"
#import "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];
_sourceDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
requestService.forceBackgroundRequests = true;
[_sourceDatacenterMtProto addMessageService:requestService];
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 NSTimeInterval timestamp, 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:(int32_t)dataId data:(NSData *)authData
{
[_sourceDatacenterMtProto stop];
_sourceDatacenterMtProto = nil;
MTContext *context = _context;
_destinationDatacenterMtProto = [[MTProto alloc] initWithContext:context datacenterId:_destinationDatacenterId usageCalculationInfo:nil];
_destinationDatacenterMtProto.useTempAuthKeys = context.useTempAuthKeys;
MTRequestMessageService *requestService = [[MTRequestMessageService alloc] initWithContext:context];
requestService.forceBackgroundRequests = true;
[_destinationDatacenterMtProto addMessageService:requestService];
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 NSTimeInterval timestamp, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,185 @@
#import "MTDiscoverDatacenterAddressAction.h"
#import "MTContext.h"
#import "MTSerialization.h"
#import "MTProto.h"
#import "MTDatacenterAddressSet.h"
#import "MTRequestMessageService.h"
#import "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] != nil)
{
_mtProto = [[MTProto alloc] initWithContext:context datacenterId:_targetDatacenterId usageCalculationInfo:nil];
_mtProto.useTempAuthKeys = useTempAuthKeys;
_requestService = [[MTRequestMessageService alloc] initWithContext:_context];
_requestService.forceBackgroundRequests = true;
[_mtProto addMessageService:_requestService];
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 NSTimeInterval completionTimestamp, 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];
}
}
- (void)contextDatacenterAuthInfoUpdated:(MTContext *)context datacenterId:(NSInteger)datacenterId authInfo:(MTDatacenterAuthInfo *)__unused authInfo
{
if (_context != context || !_awaitingAddresSetUpdate)
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

View File

@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@interface MTDropResponseContext : NSObject
@property (nonatomic, readonly) int64_t dropMessageId;
@property (nonatomic) int64_t messageId;
@property (nonatomic) int32_t messageSeqNo;
- (instancetype)initWithDropMessageId:(int64_t)dropMessageId;
@end

View File

@ -0,0 +1,17 @@
#import "MTDropResponseContext.h"
@implementation MTDropResponseContext
- (instancetype)initWithDropMessageId:(int64_t)dropMessageId
{
self = [super init];
if (self != nil)
{
_dropMessageId = dropMessageId;
}
return self;
}
@end

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,91 @@
#ifndef MTEncryption_H
#define MTEncryption_H
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
NSData *MTSha1(NSData *data);
NSData *MTSubdataSha1(NSData *data, NSUInteger offset, NSUInteger length);
NSData *MTSha256(NSData *data);
void MTRawSha1(void const *inData, NSUInteger length, void *outData);
void MTRawSha256(void const *inData, NSUInteger length, void *outData);
int32_t MTMurMurHash32(const void *bytes, int length);
void MTAesEncryptInplace(NSMutableData *data, NSData *key, NSData *iv);
void MTAesEncryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv);
void MTAesEncryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv);
void MTAesEncryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv);
void MTAesDecryptRaw(void const *data, void *outData, NSInteger length, void const *key, void const *iv);
void MTAesDecryptInplaceAndModifyIv(NSMutableData *data, NSData *key, NSMutableData *iv);
void MTAesDecryptBytesInplaceAndModifyIv(void *data, NSInteger length, NSData *key, void *iv);
NSData *MTAesEncrypt(NSData *data, NSData *key, NSData *iv);
NSData *MTAesDecrypt(NSData *data, NSData *key, NSData *iv);
NSData *MTRsaEncrypt(NSString *publicKey, NSData *data);
NSData *MTExp(NSData *base, NSData *exp, NSData *modulus);
NSData *MTModSub(NSData *a, NSData *b, NSData *modulus);
NSData *MTModMul(NSData *a, NSData *b, NSData *modulus);
NSData *MTMul(NSData *a, NSData *b);
NSData *MTAdd(NSData *a, NSData *b);
bool MTFactorize(uint64_t what, uint64_t *resA, uint64_t *resB);
bool MTIsZero(NSData *value);
NSData *MTAesCtrDecrypt(NSData *data, NSData *key, NSData *iv);
@protocol MTKeychain;
bool MTCheckIsSafeG(unsigned int g);
bool MTCheckIsSafeB(NSData *b, NSData *p);
bool MTCheckIsSafePrime(NSData *numberBytes, id<MTKeychain> keychain);
bool MTCheckIsSafeGAOrB(NSData *gAOrB, NSData *p);
bool MTCheckMod(NSData *numberBytes, unsigned int g, id<MTKeychain> keychain);
@interface MTAesCtr : NSObject
- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv decrypt:(bool)decrypt;
- (instancetype)initWithKey:(const void *)key keyLength:(int)keyLength iv:(const void *)iv ecount:(void *)ecount num:(uint32_t)num;
- (uint32_t)num;
- (void *)ecount;
- (void)getIv:(void *)iv;
- (void)encryptIn:(const unsigned char *)in out:(unsigned char *)out len:(size_t)len;
@end
uint64_t MTRsaFingerprint(NSString *key);
NSData *MTRsaEncryptPKCS1OAEP(NSString *key, NSData *data);
@interface MTBackupDatacenterAddress : NSObject
@property (nonatomic, readonly) int32_t datacenterId;
@property (nonatomic, strong, readonly) NSString *ip;
@property (nonatomic, readonly) int32_t port;
@property (nonatomic, strong, readonly) NSData *secret;
@end
@interface MTBackupDatacenterData : NSObject
@property (nonatomic, readonly) int32_t timestamp;
@property (nonatomic, readonly) int32_t expirationDate;
@property (nonatomic, strong, readonly) NSArray<MTBackupDatacenterAddress *> *addressList;
@end
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber);
NSData * _Nullable MTPBKDF2(NSData * _Nonnull data, NSData * _Nonnull salt, int rounds);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@interface MTExportedAuthorizationData : NSObject
@property (nonatomic, strong, readonly) NSData *authorizationBytes;
@property (nonatomic, readonly) int32_t authorizationId;
- (instancetype)initWithAuthorizationBytes:(NSData *)authorizationBytes authorizationId:(int32_t)authorizationId;
@end

View File

@ -0,0 +1,16 @@
#import "MTExportedAuthorizationData.h"
@implementation MTExportedAuthorizationData
- (instancetype)initWithAuthorizationBytes:(NSData *)authorizationBytes authorizationId:(int32_t)authorizationId
{
self = [super init];
if (self != nil)
{
_authorizationBytes = authorizationBytes;
_authorizationId = authorizationId;
}
return self;
}
@end

View File

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTKeychain.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTKeychain.h>
#else
# import <MtProtoKit/MTKeychain.h>
#endif
NS_ASSUME_NONNULL_BEGIN
@interface MTFileBasedKeychain : NSObject <MTKeychain>
+ (instancetype)unencryptedKeychainWithName:(NSString * _Nullable)name documentsPath:(NSString *)documentsPath;
+ (instancetype)keychainWithName:(NSString * _Nullable)name documentsPath:(NSString * _Nullable)documentsPath;
- (NSDictionary<NSString *, id> *)contentsForGroup:(NSString *)group;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,403 @@
#import "MTFileBasedKeychain.h"
#import "MTLogging.h"
#import <pthread.h>
#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t _TG_SYNCHRONIZED_##lock
#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&_TG_SYNCHRONIZED_##lock, NULL)
#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&_TG_SYNCHRONIZED_##lock);
#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&_TG_SYNCHRONIZED_##lock);
#import <CommonCrypto/CommonCrypto.h>
#import "MTEncryption.h"
static TG_SYNCHRONIZED_DEFINE(_keychains) = PTHREAD_MUTEX_INITIALIZER;
static NSMutableDictionary *keychains()
{
static NSMutableDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
TG_SYNCHRONIZED_INIT(_keychains);
dict = [[NSMutableDictionary alloc] init];
});
return dict;
}
@interface MTFileBasedKeychain ()
{
NSString *_name;
bool _encrypted;
NSData *_aesKey;
NSData *_aesIv;
NSString *_documentsPath;
TG_SYNCHRONIZED_DEFINE(_dictByGroup);
NSMutableDictionary *_dictByGroup;
}
@end
@implementation MTFileBasedKeychain
+ (instancetype)unencryptedKeychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTFileBasedKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTFileBasedKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:false];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
+ (instancetype)keychainWithName:(NSString *)name documentsPath:(NSString *)documentsPath
{
if (name == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_keychains);
MTFileBasedKeychain *keychain = [keychains() objectForKey:name];
if (keychain == nil)
{
keychain = [[MTFileBasedKeychain alloc] initWithName:name documentsPath:documentsPath encrypted:true];
[keychains() setObject:keychain forKey:name];
}
TG_SYNCHRONIZED_END(_keychains);
return keychain;
}
- (instancetype)initWithName:(NSString *)name documentsPath:(NSString *)documentsPath encrypted:(bool)encrypted
{
self = [super init];
if (self != nil)
{
TG_SYNCHRONIZED_INIT(_dictByGroup);
_dictByGroup = [[NSMutableDictionary alloc] init];
_name = name;
_documentsPath = documentsPath;
_encrypted = encrypted;
if (name != nil)
{
if (_encrypted)
{
NSMutableDictionary *keychainReadQuery = [[NSMutableDictionary alloc] initWithDictionary:@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecReturnData: (id)kCFBooleanTrue,
(__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
}];
bool upgradeEncryption = false;
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
else
{
keychainReadQuery[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainReadQuery, (CFTypeRef *)&keyData) == noErr && keyData != NULL)
{
NSData *data = (__bridge_transfer NSData *)keyData;
if (data.length == 64)
{
upgradeEncryption = true;
_aesKey = [data subdataWithRange:NSMakeRange(0, 32)];
_aesIv = [data subdataWithRange:NSMakeRange(32, 32)];
}
}
}
bool storeKey = upgradeEncryption || _aesKey == nil || _aesIv == nil;
if (_aesKey == nil || _aesIv == nil)
{
uint8_t buf[32];
int result = 0;
result = SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesKey = [[NSData alloc] initWithBytes:buf length:32];
result = SecRandomCopyBytes(kSecRandomDefault, 32, buf);
_aesIv = [[NSData alloc] initWithBytes:buf length:32];
}
NSMutableData *newKeyData = [[NSMutableData alloc] init];
[newKeyData appendData:_aesKey];
[newKeyData appendData:_aesIv];
if (storeKey)
{
SecItemDelete((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: upgradeEncryption ? (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly
#endif
});
SecItemAdd((__bridge CFDictionaryRef)@{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"org.mtproto.MTKeychain",
(__bridge id)kSecAttrAccount: [[NSString alloc] initWithFormat:@"MTKeychain:%@", name],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
#endif
(__bridge id)kSecValueData: newKeyData
}, NULL);
}
}
}
}
return self;
}
- (NSString *)filePathForName:(NSString *)name group:(NSString *)group
{
static NSString *dataDirectory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
#if TARGET_OS_IPHONE
dataDirectory = [_documentsPath stringByAppendingPathComponent:@"mtkeychain"];
#elif TARGET_OS_MAC
NSString *applicationSupportPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
NSString *applicationName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
dataDirectory = [[applicationSupportPath stringByAppendingPathComponent:applicationName] stringByAppendingPathComponent:@"mtkeychain"];
#else
# error "Unsupported OS"
#endif
__autoreleasing NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:dataDirectory withIntermediateDirectories:true attributes:nil error:&error];
if (error != nil) {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error creating keychain directory: %@]", error);
}
}
});
return [dataDirectory stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%@_%@.bin", name, group]];
}
- (void)_loadKeychainIfNeeded:(NSString *)group
{
if (_dictByGroup[group] == nil)
{
if (_name != nil)
{
NSData *data = [[NSData alloc] initWithContentsOfFile:[self filePathForName:_name group:group]];
if (data != nil && data.length >= 4)
{
uint32_t length = 0;
[data getBytes:&length range:NSMakeRange(0, 4)];
uint32_t paddedLength = length;
while (paddedLength % 16 != 0)
{
paddedLength++;
}
if (data.length == 4 + paddedLength || data.length == 4 + paddedLength + 4)
{
NSMutableData *encryptedData = [[NSMutableData alloc] init];
[encryptedData appendData:[data subdataWithRange:NSMakeRange(4, paddedLength)]];
NSMutableData *decryptedData = nil;
if (_encrypted) {
decryptedData = [[NSMutableData alloc] initWithData:MTAesDecrypt(encryptedData, _aesKey, _aesIv)];
} else {
decryptedData = encryptedData;
}
[decryptedData setLength:length];
bool hashVerified = true;
if (data.length == 4 + paddedLength + 4)
{
int32_t hash = 0;
[data getBytes:&hash range:NSMakeRange(4 + paddedLength, 4)];
int32_t decryptedHash = MTMurMurHash32(decryptedData.bytes, (int)decryptedData.length);
if (hash != decryptedHash)
{
if (MTLogEnabled()) {
MTLog(@"[MTKeychain invalid decrypted hash]");
}
hashVerified = false;
}
}
if (hashVerified)
{
@try
{
id object = [NSKeyedUnarchiver unarchiveObjectWithData:decryptedData];
if ([object respondsToSelector:@selector(objectForKey:)] && [object respondsToSelector:@selector(setObject:forKey:)])
_dictByGroup[group] = object;
else {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain invalid root object %@]", object);
}
}
}
@catch (NSException *e)
{
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error parsing keychain: %@]", e);
}
}
}
}
else {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error loading keychain: expected data length %d, got %d]", 4 + (int)paddedLength, (int)data.length);
}
}
}
}
if (_dictByGroup[group] == nil)
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
}
}
- (void)_storeKeychain:(NSString *)group
{
if (_dictByGroup[group] != nil && _name != nil)
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_dictByGroup[group]];
if (data != nil)
{
NSMutableData *encryptedData = [[NSMutableData alloc] initWithData:data];
int32_t hash = MTMurMurHash32(encryptedData.bytes, (int)encryptedData.length);
while (encryptedData.length % 16 != 0)
{
uint8_t random = 0;
arc4random_buf(&random, 1);
[encryptedData appendBytes:&random length:1];
}
if (_encrypted)
MTAesEncryptInplace(encryptedData, _aesKey, _aesIv);
uint32_t length = (uint32_t)data.length;
[encryptedData replaceBytesInRange:NSMakeRange(0, 0) withBytes:&length length:4];
[encryptedData appendBytes:&hash length:4];
NSString *filePath = [self filePathForName:_name group:group];
if (![encryptedData writeToFile:filePath atomically:true]) {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error writing keychain to file]");
}
}
else
{
#if TARGET_OS_IPHONE
__autoreleasing NSError *error = nil;
[[NSURL fileURLWithPath:filePath] setResourceValue:[NSNumber numberWithBool:true] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (error != nil) {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error setting \"exclude from backup\" flag]");
}
}
#endif
}
}
else {
if (MTLogEnabled()) {
MTLog(@"[MTKeychain error serializing keychain]");
}
}
}
}
- (void)setObject:(id)object forKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (object == nil || aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
_dictByGroup[group][aKey] = object;
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (id)objectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return nil;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
id result = _dictByGroup[group][aKey];
TG_SYNCHRONIZED_END(_dictByGroup);
return result;
}
- (void)removeObjectForKey:(id<NSCopying>)aKey group:(NSString *)group
{
if (aKey == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
[_dictByGroup[group] removeObjectForKey:aKey];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (void)dropGroup:(NSString *)group
{
if (group == nil)
return;
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
_dictByGroup[group] = [[NSMutableDictionary alloc] init];
[self _storeKeychain:group];
TG_SYNCHRONIZED_END(_dictByGroup);
}
- (NSDictionary<NSString *, id> *)contentsForGroup:(NSString *)group {
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
TG_SYNCHRONIZED_BEGIN(_dictByGroup);
[self _loadKeychainIfNeeded:group];
[_dictByGroup[group] enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, __unused BOOL * _Nonnull stop) {
result[key] = obj;
}];
TG_SYNCHRONIZED_END(_dictByGroup);
return result;
}
@end

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
@class MTSignal;
@interface MTHttpRequestOperation : NSObject
+ (MTSignal *)dataForHttpUrl:(NSURL *)url;
+ (MTSignal *)dataForHttpUrl:(NSURL *)url headers:(NSDictionary *)headers;
@end

View File

@ -0,0 +1,53 @@
#import "MTHttpRequestOperation.h"
#import "../thirdparty/AFNetworking/AFHTTPRequestOperation.h"
#if defined(MtProtoKitDynamicFramework)
# import <MTProtoKitDynamic/MTDisposable.h>
# import <MTProtoKitDynamic/MTSignal.h>
#elif defined(MtProtoKitMacFramework)
# import <MTProtoKitMac/MTDisposable.h>
# import <MTProtoKitMac/MTSignal.h>
#else
# import <MtProtoKit/MTDisposable.h>
# import <MtProtoKit/MTSignal.h>
#endif
@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];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setSuccessCallbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
[operation setFailureCallbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
[operation setCompletionBlockWithSuccess:^(__unused NSOperation *operation, __unused id responseObject)
{
[subscriber putNext:[(AFHTTPRequestOperation *)operation responseData]];
[subscriber putCompletion];
} failure:^(__unused NSOperation *operation, __unused NSError *error)
{
[subscriber putError:nil];
}];
[operation start];
__weak AFHTTPRequestOperation *weakOperation = operation;
return [[MTBlockDisposable alloc] initWithBlock:^
{
__strong AFHTTPRequestOperation *strongOperation = weakOperation;
[strongOperation cancel];
}];
}];
}
@end

View File

@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface MTIncomingMessage : NSObject
@property (nonatomic, readonly) int64_t messageId;
@property (nonatomic, readonly) int32_t seqNo;
@property (nonatomic, readonly) int64_t authKeyId;
@property (nonatomic, readonly) int64_t sessionId;
@property (nonatomic, readonly) int64_t salt;
@property (nonatomic, readonly) NSTimeInterval timestamp;
@property (nonatomic, readonly) NSInteger size;
@property (nonatomic, strong, readonly) id body;
- (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;
@end

View File

@ -0,0 +1,24 @@
#import "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

View File

@ -0,0 +1,25 @@
#import <Foundation/Foundation.h>
@interface MTInputStream : NSObject
- (instancetype)initWithData:(NSData *)data;
- (NSInputStream *)wrappedInputStream;
- (int32_t)readInt32;
- (int32_t)readInt32:(bool *)failed __attribute__((nonnull(1)));
- (int64_t)readInt64;
- (int64_t)readInt64:(bool *)failed __attribute__((nonnull(1)));
- (double)readDouble;
- (double)readDouble:(bool *)failed __attribute__((nonnull(1)));
- (NSData *)readData:(int)length;
- (NSData *)readData:(int)length failed:(bool *)failed __attribute__((nonnull(2)));
- (NSMutableData *)readMutableData:(NSUInteger)length;
- (NSMutableData *)readMutableData:(NSUInteger)length failed:(bool *)failed __attribute__((nonnull(2)));
- (NSString *)readString;
- (NSString *)readString:(bool *)failed __attribute__((nonnull(1)));
- (NSData *)readBytes;
- (NSData *)readBytes:(bool *)failed __attribute__((nonnull(1)));
@end

View File

@ -0,0 +1,429 @@
#import <Foundation/Foundation.h>
#import "MTLogging.h"
#import "MTInputStream.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

View File

@ -0,0 +1,51 @@
#ifndef MtProtoKit_MTInternalId_h
#define MtProtoKit_MTInternalId_h
#import <libkern/OSAtomic.h>
#define MTInternalId(name) MT##name##InternalId
#define MTInternalIdClass(name) \
@interface MT##name##InternalId : NSObject <NSCopying> \
{ \
NSUInteger _value; \
} \
\
@end \
\
@implementation MT##name##InternalId \
\
- (instancetype)init \
{ \
self = [super init]; \
if (self != nil) \
{ \
static int32_t nextValue = 1; \
_value = OSAtomicIncrement32(&nextValue); \
} \
return self; \
} \
\
- (BOOL)isEqual:(id)object \
{ \
return [object isKindOfClass:[MT##name##InternalId class]] && ((MT##name##InternalId *)object)->_value == _value; \
} \
\
- (NSUInteger)hash \
{ \
return _value; \
} \
\
- (instancetype)copyWithZone:(NSZone *)__unused zone \
{ \
MT##name##InternalId *another = [[MT##name##InternalId alloc] init]; \
if (another != nil) \
another->_value = _value; \
return another; \
} \
\
@end
#endif

View File

@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface MTInternalMessageParser : NSObject
+ (id)parseMessage:(NSData *)data;
+ (id)unwrapMessage:(NSData *)data;
@end

View File

@ -0,0 +1,752 @@
#import "MTInternalMessageParser.h"
#import "MTBufferReader.h"
#import "MTResPqMessage.h"
#import "MTRpcResultMessage.h"
#import "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 "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;
unsigned char output[kMemoryChunkSize];
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

View File

@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@protocol MTKeychain <NSObject>
- (void)setObject:(id)object forKey:(NSString *)aKey group:(NSString *)group;
- (id)objectForKey:(NSString *)aKey group:(NSString *)group;
- (void)removeObjectForKey:(NSString *)aKey group:(NSString *)group;
- (void)dropGroup:(NSString *)group;
@end

View File

@ -0,0 +1,3 @@
#import "MTKeychain.h"

View File

@ -0,0 +1,23 @@
#ifndef MTLogging_H
#define MTLogging_H
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
bool MTLogEnabled();
void MTLog(NSString *format, ...);
void MTShortLog(NSString *format, ...);
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args));
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args));
void MTLogSetEnabled(bool);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,41 @@
#import "MTLogging.h"
static void (*loggingFunction)(NSString *, va_list args) = NULL;
static void (*shortLoggingFunction)(NSString *, va_list args) = 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) {
loggingFunction(format, L);
}
va_end(L);
}
void MTShortLog(NSString *format, ...) {
va_list L;
va_start(L, format);
if (shortLoggingFunction != NULL) {
shortLoggingFunction(format, L);
}
va_end(L);
}
void MTLogSetLoggingFunction(void (*function)(NSString *, va_list args)) {
loggingFunction = function;
}
void MTLogSetShortLoggingFunction(void (*function)(NSString *, va_list args)) {
shortLoggingFunction = function;
}
void MTLogSetEnabled(bool enabled) {
MTLogEnabledValue = enabled;
}

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

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

View File

@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
@interface MTMessageEncryptionKey : NSObject
@property (nonatomic, strong, readonly) NSData *key;
@property (nonatomic, strong, readonly) NSData *iv;
+ (instancetype)messageEncryptionKeyForAuthKey:(NSData *)authKey messageKey:(NSData *)messageKey toClient:(bool)toClient;
+ (instancetype)messageEncryptionKeyV2ForAuthKey:(NSData *)authKey messageKey:(NSData *)messageKey toClient:(bool)toClient;
- (instancetype)initWithKey:(NSData *)key iv:(NSData *)iv;
@end

View File

@ -0,0 +1,123 @@
#import "MTMessageEncryptionKey.h"
#import "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

View File

@ -0,0 +1,41 @@
#import <Foundation/Foundation.h>
@class MTProto;
@class MTIncomingMessage;
@class MTMessageTransaction;
@class MTApiEnvironment;
@protocol MTMessageService <NSObject>
@optional
- (void)mtProtoWillAddService:(MTProto *)mtProto;
- (void)mtProtoDidAddService:(MTProto *)mtProto;
- (void)mtProtoDidRemoveService:(MTProto *)mtProto;
- (void)mtProtoPublicKeysUpdated:(MTProto *)mtProto datacenterId:(NSInteger)datacenterId publicKeys:(NSArray<NSDictionary *> *)publicKeys;
- (MTMessageTransaction *)mtProtoMessageTransaction:(MTProto *)mtProto;
- (void)mtProtoDidChangeSession:(MTProto *)mtProto;
- (void)mtProtoServerDidChangeSession:(MTProto *)mtProto firstValidMessageId:(int64_t)firstValidMessageId otherValidMessageIds:(NSArray *)otherValidMessageIds;
- (void)mtProto:(MTProto *)mtProto receivedMessage:(MTIncomingMessage *)message;
- (void)mtProto:(MTProto *)mtProto receivedQuickAck:(int32_t)quickAckId;
- (void)mtProto:(MTProto *)mtProto transactionsMayHaveFailed:(NSArray *)transactionIds;
- (void)mtProtoAllTransactionsMayHaveFailed:(MTProto *)mtProto;
- (void)mtProto:(MTProto *)mtProto messageDeliveryFailed:(int64_t)messageId;
- (void)mtProto:(MTProto *)mtProto messageDeliveryConfirmed:(NSArray *)messageIds;
- (void)mtProto:(MTProto *)mtProto messageResendRequestFailed:(int64_t)messageId;
- (void)mtProto:(MTProto *)mtProto protocolErrorReceived:(int32_t)errorCode;
- (bool)mtProto:(MTProto *)mtProto shouldRequestMessageWithId:(int64_t)responseMessageId inResponseToMessageId:(int64_t)messageId currentTransactionId:(id)currentTransactionId;
- (void)mtProto:(MTProto *)mtProto updateReceiveProgressForToken:(id)progressToken progress:(float)progress packetLength:(NSInteger)packetLength;
- (void)mtProtoNetworkAvailabilityChanged:(MTProto *)mtProto isNetworkAvailable:(bool)isNetworkAvailable;
- (void)mtProtoConnectionStateChanged:(MTProto *)mtProto isConnected:(bool)isConnected;
- (void)mtProtoConnectionContextUpdateStateChanged:(MTProto *)mtProto isUpdatingConnectionContext:(bool)isUpdatingConnectionContext;
- (void)mtProtoServiceTasksStateChanged:(MTProto *)mtProto isPerformingServiceTasks:(bool)isPerformingServiceTasks;
- (void)mtProtoAuthTokenUpdated:(MTProto *)mtProto;
- (void)mtProtoApiEnvironmentUpdated:(MTProto *)mtProto apiEnvironment:(MTApiEnvironment *)apiEnvironment;
@end

View File

@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
@interface MTMessageTransaction : NSObject
@property (nonatomic, strong, readonly) id internalId;
/*
* Can be invoked multiple times in case message transaction maps to multiple different transport transactions
*/
@property (nonatomic, copy) void (^completion)(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, NSDictionary *messageInternalIdToQuickAckId);
@property (nonatomic, copy) void (^prepared)(NSDictionary *messageInternalIdToPreparedMessage);
@property (nonatomic, copy) void (^failed)();
@property (nonatomic, strong) NSArray *messagePayload;
@property (nonatomic) bool allowServiceMode;
@property (nonatomic) bool requiresEncryption;
- (instancetype)initWithMessagePayload:(NSArray *)messagePayload prepared:(void (^)(NSDictionary *messageInternalIdToPreparedMessage))prepared failed:(void (^)())failed completion:(void (^)(NSDictionary *messageInternalIdToTransactionId, NSDictionary *messageInternalIdToPreparedMessage, NSDictionary *messageInternalIdToQuickAckId))completion;
@end

View File

@ -0,0 +1,26 @@
#import "MTMessageTransaction.h"
#import "MTInternalId.h"
MTInternalIdClass(MTMessageTransaction)
@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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgContainerMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messages;
- (instancetype)initWithMessages:(NSArray *)messages;
@end

View File

@ -0,0 +1,15 @@
#import "MTMsgContainerMessage.h"
@implementation MTMsgContainerMessage
- (instancetype)initWithMessages:(NSArray *)messages
{
self = [super init];
if (self != nil)
{
_messages = messages;
}
return self;
}
@end

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface MTMsgResendReqMessage : NSObject
@property (nonatomic, strong, readonly) NSArray *messageIds;
- (instancetype)initWithMessageIds:(NSArray *)messageIds;
@end

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