iOS Resolve DNS программно иногда возвращает недопустимые IP-адреса

Привет,

Я работаю над приложением для iOS, которое требует программного разрешения DNS.

Считайте это прокси-сервером для разрешения всех DNS-запросов на iPhone. Я получаю DNS-запросы от каждого приложения на iPhone и отправляю обратно соответствующий IPList.

Я пробовал несколько методов, но у обоих были одинаковые ответы. Тот, который я решил использовать, приведен ниже функции resolveHost, написанной на objective-c и c. Я вызываю этот метод из быстрого кода.

Вот как я звоню из swift, а также делюсь образцом хоста / URL, значением хоста может быть любой домен («google.com, apple.com и т. Д.») Или домен / хост в результате следов при открытии сайта. в mkwebview

let host = "www.opera.com"

let ipArray = ResolveUtil (). resolveHost (host, usingDNSServer: "8.8.8.8") as! [Нить]

В частности, приложение Facebook плохо работает с IP-адресами, возвращаемыми функцией resolveHost

Под "плохой работой" я подразумеваю, что приложение не подключается к IP-адресам, возвращаемым функциями.

Иногда он возвращает 192.16.192.16 как часть других IP-адресов для некоторых хостов / доменов. Что это за IP?

- (NSArray*)resolveHost:(NSString *)host usingDNSServer:(NSString *)dnsServer
{

    NSMutableArray* result = [[NSMutableArray alloc] init];
    struct __res_state res;
    setup_dns_server(&res, [dnsServer cStringUsingEncoding:NSASCIIStringEncoding]);
    int count;
    char** ips = query_ips(&res, [host cStringUsingEncoding:NSUTF8StringEncoding], &count);
    for (int i=0; i<count; i++){
        [result addObject:[[NSString alloc] initWithCString:ips[i] encoding:NSASCIIStringEncoding]];
    }

    for (int i=0; i<count; i++){

        free(ips[i]);
    }
    free(ips);
    ips = NULL;

    return result;

}

char ** query_ips(res_state res, const char *host, int* count)
{
    u_char answer[NS_PACKETSZ];
    int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));

    ns_msg handle;
    ns_initparse(answer, len, &handle);

    int messageCount = ns_msg_count(handle, ns_s_an);
    *count = messageCount;
    char **ips = malloc(messageCount * sizeof(char *));

    for (int i=0; i < messageCount; i++) {
        ips[i] = malloc(16 * sizeof(char));
        memset(ips[i], '\0', sizeof(16));
        ns_rr rr;
        if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
            strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
        }
    }
    return ips;
}

Другой метод

func resolveIp(_ hostUrl:String) -> [String]{
        var ips:[String] = [String]()
        let host = CFHostCreateWithName(nil,hostUrl as CFString).takeRetainedValue()
        CFHostStartInfoResolution(host, .addresses, nil)
        var success: DarwinBoolean = false
        if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
            for case let theAddress as NSData in addresses {
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
                               &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
                    let numAddress = String(cString: hostname)
                    ips.append(numAddress)
                }
            }
        }
        Logger.info("\(#function) validIPs:\(ips.joined(separator: "-")) url:\(hostUrl)")
        return ips
    }

person Bhatti    schedule 18.06.2020    source источник
comment
Как это «не работает»? Какой результат вы получите и чего ожидаете?   -  person Martin R    schedule 18.06.2020
comment
Плюс добавьте код, как вы используете метод resolveHost:usingDNSServer: - особенно интересны значения аргументов.   -  person zrzka    schedule 18.06.2020
comment
Вы звоните resolveHost2:..., но вы указали код resolveHost:.... Какая разница? Передача url аргументу host также выглядит подозрительно. Какое значение имеет url?   -  person zrzka    schedule 18.06.2020
comment
@zrzka Я предоставил образец ввода в вопросе, любая другая информация о несоответствии - это просто ошибка типа, идея в том, что я вызываю эту функцию для разрешения хоста / доменов / URL-адресов на IP-адреса. В реальном коде я уверен, что вызываю правильную функцию с правильным вводом, чтобы отличаться от реального кода, я внес здесь некоторые изменения в имена переменных и функций.   -  person Bhatti    schedule 18.06.2020
comment
IP-адреса @MartinR, возвращенные из функции, никогда не были подключены только для нескольких доменов / хостов / URL-адресов, иначе он работает нормально   -  person Bhatti    schedule 18.06.2020


Ответы (2)


192.16.192.16

Что это за IP? Совершенно действующий IPv4. Он возвращается к basento.nikhef.nl.

Почему возвращается?

Я не знаю. Может быть, см. resolv.h:

 * Mac OS supports a DNS query routing API (see <dns.h>) which is used by
 * most system services to access DNS.  The BIND-9 APIs described here are
 * a lower-level that does not do query routing or search amongst multiple
 * resolver clients.  The results of DNS queries from this API may differ
 * significantly from the results of queries sent to the <dns.h> API.  We
 * strongly encourage developers to use higher-level APIs where possible.

Под неработоспособностью я подразумеваю, что приложение не подключается к IP-адресам, возвращаемым функциями.

Это не имеет ничего общего с разрешением имени / IP-адреса.

Проблема может быть в другом. Ваш провайдер может заблокировать его, на IP-адресе не запущена никакая служба, вам не разрешен доступ к нему ... По многим причинам.

В частности, приложение Facebook не работает с IP-адресами, возвращаемыми функцией resolveHost.

Для меня это вообще не имеет смысла. У вас есть собственное приложение, вы разрешаете в нем IP-адреса, а затем говорите, что оно не работает с Facebook. Честно говоря, я понятия не имею, что вы имеете в виду.


Почему я отвечаю на этот вопрос? Что ж, вам не следует слепо копировать и вставлять код с других вопросов о переполнении стека или любых других сайтов. Провел исследование, и оно похоже на копирование и вставку некоторых других ответов.

Почему? Код в вашем вопросе не обрабатывает ошибки, не соответствует документации ... Это чистая удача, что он работает для вас.

Что, если это твоя проблема? Вы когда-нибудь рассматривали этот вариант?

Вот пример Resolver, который вы можете использовать / тестировать с вашими условиями. Это может решить ваши проблемы, а может и не решить.

#import <resolv.h>
@import Darwin.POSIX.arpa;

@interface Resolver: NSObject

- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server;
- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host;

@end

@implementation Resolver {
    struct __res_state *state;
}

- (void)dealloc {
    if (state != NULL) {
        // man 3 resolver:
        //
        // res_ndestroy() should be call to free memory allocated by res_ninit() after last use.
        if ((state->options & RES_INIT) == RES_INIT) {
            res_ndestroy(state);
        }
        free(state);
        state = NULL;
    }
}

- (nullable instancetype)initWithDNSServer:(nonnull NSString *)server {
    if ((self = [super init]) == nil) {
        return nil;
    }

    // man 3 resolver:
    //
    // The memory referred to by statp must be set to all zeros prior
    // to the first call to res_ninit(). res_ndestroy() should be call to free memory
    // allocated by res_ninit() after last use.
    if ((state = calloc(1, sizeof(*state))) == NULL) {
        return nil;
    }

    // 0 success
    if (res_ninit(state) != 0) {
        return nil;
    }

    // Avoid calling inet_aton later with NULL if we can't convert it to ASCII
    if (![server canBeConvertedToEncoding:NSASCIIStringEncoding]) {
        return nil;
    }

    struct in_addr addr;

    // man 3 inet_aton:
    //
    // It returns 1 if the string was successfully interpreted ...
    if (inet_aton([server cStringUsingEncoding:NSASCIIStringEncoding], &addr) != 1) {
        return nil;
    }

    state->nsaddr_list[0].sin_addr = addr;
    state->nsaddr_list[0].sin_family = AF_INET;
    state->nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT);
    state->nscount = 1;

    return self;
}

- (nullable NSArray<NSString *> *)resolveHost:(nonnull NSString *)host {
    // Avoid calling res_nquery with NULL
    if (![host canBeConvertedToEncoding:NSASCIIStringEncoding]) {
        return nil;
    }

    u_char answer[NS_PACKETSZ];

    int len = res_nquery(state, [host cStringUsingEncoding:NSASCIIStringEncoding],
                         ns_c_in, ns_t_a, answer, sizeof(answer));

    // -1 = error
    if (len == -1) {
        return nil;
    }

    ns_msg handle;

    // 0 success, -1 error
    if (ns_initparse(answer, len, &handle) != 0) {
        return nil;
    }

    u_int16_t count = ns_msg_count(handle, ns_s_an);
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0 ; i < count ; i++) {
        ns_rr rr;
        // 0 success, -1 error
        if (ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
            char *address = inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr));

            if (address == NULL) {
                continue;
            }

            NSString *ip = [NSString stringWithCString:address
                                              encoding:NSASCIIStringEncoding];
            [result addObject:ip];
        }
    }

    return result;
}

@end

Вы можете использовать это так:

Resolver *resolver = [[Resolver alloc] initWithDNSServer:@"8.8.8.8"];
NSLog(@"%@", [resolver resolveHost:@"www.opera.com"]);

Выход:

(
    "13.102.114.111",
    "52.57.141.185",
    "18.196.127.98"
)
person zrzka    schedule 18.06.2020
comment
да, вы правы, я нашел этот код в Интернете, я поделился здесь, потому что он не работал у меня, как ожидалось. Что касается обработки ошибок, я не вносил никаких изменений в код, который я нашел, но я веду журнал, если возвращается пустой массив. Единственное, что я не упомянул в вопросе, это NEDNSProxyProvider, поэтому просто узнаю, как он работает, в прокси-сервере я получаю потоки, которые нужно разрешить, и я использую эту функцию для разрешения, и я получаю трафик от всех приложений, другие приложения работают нормально, но два из них не YouTube и Facebook. Используя Wireshark, я подтвердил, что приложение отправляет запрос на подключение, но не подключается - person Bhatti; 19.06.2020
comment
Проверка пустого массива не является правильной обработкой ошибок. Сравните свой и мой код - есть масса мест, где он может выйти из строя. Вам нужна помощь, но вы не делитесь тем, что делаете. Посмотрите на историю редактирования вашего вопроса - 8 правок, добавленные новые вещи, уточнения ... и теперь вы говорите, что играете с NEDNSProxyProvider. Что дальше? Еще один сюрприз? Извините за этот ответ, но вы должны обновить свой вопрос, указав все, что вы делаете, с кодом, как вы его используете, с минимальным воспроизводимым примером ... В противном случае мы тратим зря время друг друга. - person zrzka; 19.06.2020
comment
Большинство правок было связано с орфографическими и грамматическими ошибками, некоторые из них были основаны на ваших запросах, поэтому я подумал, что должен предоставить некоторые дополнительные сведения, но есть предел, сколько деталей я могу поделиться. Считаете ли вы неэтичным обращаться за помощью, не разделяя всей идеи / концепции? Я все же добавил пару строк для уточнения. Большое спасибо за вашу помощь и предложения, я очень благодарен за ваше терпение - person Bhatti; 19.06.2020
comment
Я не сужу, этично это или нет, и меня не интересует идея / концепция в целом. Дело строго техническое - у вас есть проблема, но для ее решения вы должны предоставить все - что вы делаете, когда вы это делаете, как это влияет на Facebook (все еще не ясно из самого вопроса) , ... - другими словами - предоставить контекст. Вы упомянули NEDNSProxyProvider сейчас, но это должно было быть упомянуто в том же самом начале. Опять же, дело не в том, что кто-то хочет узнать о вашей идее, но все детали и контекст имеют значение, если вам нужна помощь. - person zrzka; 19.06.2020

Возможно, я не объяснил формулировку проблемы в моем первоначальном вопросе, но мне удалось исправить ошибку, поэтому я подумал, что должен написать здесь свои выводы. Мое приложение работает как DNS-прокси, поэтому его основная задача заключалась в разрешении доменов и возвращении IP-адресов.

Я использовал функцию resolveHost для разрешения IP. У этой функции есть все проблемы, упомянутые zrzka, поэтому, если кто-то хочет использовать, пожалуйста, примите во внимание его моменты.

Проблема, с которой я столкнулся, заключалась в том, что функция возвращает несколько IP-адресов для определенных хостов / доменов, которые не кажутся действительными, я говорю недействительными, потому что это не были IP-адреса с проверкой связи, и из Wireshark я подтвердил, что соединение на этих IP-адресах было неудачным, даже если возвращенный IPList содержит действительный IP-адрес по некоторому индексу, это все еще вызывало ненужную задержку из-за первой попытки неправильных IP-адресов, поскольку они находятся перед действительными IP-адресами в списке.

В ходе дальнейшего расследования я узнал, что эти недопустимые IP-адреса были против типа ответа CNAME, который отображает псевдоним в записи DNS, я не знаю, должен ли я все равно называть их недействительными или нет, но их игнорирование помогло мне. Теперь я принимаю только ответы типа A или AAAA из ответа DNS. Я добился этого простой проверкой следующей функции.

char ** query_ips(res_state res, const char *host, int* count)
{
    u_char answer[NS_PACKETSZ];
    int len = res_nquery(res, host, ns_c_in, ns_t_a, answer, sizeof(answer));

    ns_msg handle;
    ns_initparse(answer, len, &handle);

    int messageCount = ns_msg_count(handle, ns_s_an);
    *count = messageCount;
    char **ips = malloc(messageCount * sizeof(char *));

    for (int i=0; i < messageCount; i++) {
        ips[i] = malloc(16 * sizeof(char));
        memset(ips[i], '\0', sizeof(16));
        ns_rr rr;
        if(ns_parserr(&handle, ns_s_an, i, &rr) == 0) {
            if (1 == rr.type || 28 == rr.type) // here is the new check
              strcpy(ips[i], inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr)));
        }
    }
    return ips;
}
person Bhatti    schedule 20.06.2020