Home telegram - mtDiscoverConnectionSignals
Post
Cancel

telegram - mtDiscoverConnectionSignals

Payload

// payloadData
+ (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
        0xea, 0x99, 0x1f, 0x74, // req_pq_multi
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // nonce
    };
    
    //0xbe7e8ef1 —> 0x741f99ea
    
    MTPayloadData payloadData;
    arc4random_buf(&payloadData.nonce, 16);
    if (outPayloadData)
        *outPayloadData = payloadData;
    
    int64_t messageId = (int64_t)([[NSDate date] timeIntervalSince1970] * 4294967296);
    memcpy(reqPqBytes + 8, &messageId, 8);
    
    memcpy(reqPqBytes + 8 + 8 + 4 + 4, payloadData.nonce, 16);
    
    NSMutableData *data = [[NSMutableData alloc] initWithBytes:reqPqBytes length:sizeof(reqPqBytes)];
    
    NSData *secret = address.secret;
    if (context.apiEnvironment.socksProxySettings != nil) {
        if (context.apiEnvironment.socksProxySettings.secret != nil) {
            secret = context.apiEnvironment.socksProxySettings.secret;
        }
    }
    
    bool extendedPadding = false;
    if (secret != nil) {
        MTProxySecret *parsedSecret = [MTProxySecret parseData:secret];
        if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
            extendedPadding = true;
        }
    }
    
    if (extendedPadding) {
        uint32_t paddingSize = arc4random_uniform(128);
        if (paddingSize != 0) {
            uint8_t padding[128];
            arc4random_buf(padding, paddingSize);
            [data appendBytes:padding length:paddingSize];
        }
    }
    return data;
}

isResponseValid

+ (bool)isResponseValid:(NSData *)data payloadData:(MTPayloadData)payloadData {
    if (data.length >= 84) {
        uint8_t zero[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
// hcl 新的握手协议
        //resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
        //resPQ_ibi#0a55a935 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ_ibi;
        //uint8_t resPq[] = { 0x63, 0x24, 0x16, 0x05 };
        uint8_t resPq[] = { 0x35, 0xa9, 0x55, 0x0a };
        
        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;
}

Ipv6

+ (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;
}

Test tcp connection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
+ (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]];
}

Discover Scheme from the candidates

+ (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])
        {
          // 5秒超时发送nil;
          // 出错直接complete
            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
        {
          // 5秒超时发送nil;
          // 出错直接complete
            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];
            
// hcl 游戏盾
            //NSArray *alternatePorts = @[@80, @5222];
            NSArray *alternatePorts = @[];
            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];
                }
            }
        }
    }
    
    // 1秒没成功,直接complete并重新开始
    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];
    
  // 30秒没成功,直接complete并重新开始
    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])
        {
          //is not optimal, 先用,5秒后执行optimal discover 逻辑
            return [[MTSignal single:scheme] then:[optimalSignal delay:5.0 onQueue:[MTQueue concurrentDefaultQueue]]];
        }
        else
          // 成功找到optimal
            return [MTSignal single:scheme];
    }];
    
    return [signal catch:^MTSignal *(id error) {
        return [MTSignal complete];
    }];
}
This post is licensed under CC BY 4.0 by the author.