node.jsdnsreverse-dns

Node.JS dns.reverse multiple nameservers


I have an issue with my Node.JS application.
For some reason, dns.reverse is only working with the first nameserver specified and ignoring the second one.

I have the following piece of code:

import Resolver from '~/classes/resolver';

Resolver.addNameServer('192.168.2.1', '192.168.1.254').boot();

Resolver.hostnameFromIP('192.168.1.30').then((hostname: string) => {
    console.log(hostname);
}).catch((error) => {
    console.log(error); // We are going to end up here as the 192.168.2.1 is the first and only nameserver which will be queried
});

And here is Resolver class:

import q, { Deferred } from 'q';
import dns from 'dns';
import { ResolverInterface } from '~/classes/resolver/interfaces';

/**
 * Resolver class
 */
class Resolver implements ResolverInterface {

    /**
     * List of nameservers used for hostname resolution
     * @type { string[] }
     */
    public nameservers: string[] = [];

    /**
     * Add nameserver for resolver to use
     * @param { string } nameserver
     * @return { Resolver }
     */
    public addNameServer(...nameserver: string[]) : Resolver {
        this.nameservers.push(...nameserver);
        return this;
    }

    /**
     * Initialize resolver
     * @return { void }
     */
    public boot() : void {
        if (this.nameservers.length === 0) {
            this.initializeWithDefaultNameServers();
        }
        dns.setServers(this.nameservers);
    }

    /**
     * Resolve hostname from the IP address
     * @param { string } ip
     * @return { q.Promise<string> }
     */
    public hostnameFromIP(ip: string) : q.Promise<string> {
        const deferred: Deferred<any> = q.defer();
        dns.reverse(ip, (error: NodeJS.ErrnoException | null, hostnames: string[]) => {
            const defaultErrorMessage: string = 'Unable to resolve hostname';
            if (error) {
                return deferred.reject(defaultErrorMessage);
            }
            let hostname: string = hostnames.length === 0 ? defaultErrorMessage : hostnames[0];
            hostname = hostname.replace('.localdomain', '').trim();
            deferred.resolve(hostname);
        });
        return deferred.promise as q.Promise<string>;
    }

    /**
     * Initialize resolver with default nameservers
     * @return { void }
     * @private
     */
    private initializeWithDefaultNameServers() : void {
        const nameservers: string[] = [
            '192.168.0.1',
            '192.168.1.1',
            '192.168.2.1',
        ];
        nameservers.forEach((nameserver: string) : void => {
            this.nameservers.push(nameserver);
        });
    }

}



export default new Resolver;

Expected behavior: Application should go through all specified nameservers to resolve the hostname for specified IP address

Actual behavior: Depending on which nameserver is first, only that nameserver will be queried for the hostname.
If 192.168.2.1 is first, i can query data for 192.168.2.10, but i cannot do that for 192.168.1.30.
If 192.168.1.254 is first, i can query data for 192.168.1.30, but i cannot do that for 192.168.2.10.

Is there a way to use all specified nameservers while doing reverse hostname lookup using dns.reverse in Node.JS?

Thanks for help to Jorge Fuentes González, here is the version i've ended up using, at least it works for 2 nameservers.

     /**
     * Resolve hostname from IP address
     * @param { string } ip
     * @return { Promise<string> }
     */
    public async hostnameFromIP(ip: string) : Promise<string> {
        return await this.resolveForHostnameFromIP(ip)
            .then((hostname: string): string => hostname)
            .catch(async (error: string): Promise<string> => {
                const indexOf: number = this.nameservers.indexOf(this.currentNameServer);
                const newNameserverIndex: number = indexOf + 1;
                if (newNameserverIndex <= this.nameservers.length - 1) {
                    this.currentNameServer = this.nameservers[indexOf + 1];
                    this.setCurrentNameServerValue();
                    return await this.hostnameFromIP(ip).then((hostname: string): string => hostname);
                }
                return error;
            });
    }

    /**
     * Helper method to resolve hostname from the IP address
     * @param { string } ip
     * @return { q.Promise<string> }
     */
    private resolveForHostnameFromIP(ip: string) : q.Promise<string> {
        const deferred: Deferred<any> = q.defer();
        this.resolver.reverse(ip, (error: NodeJS.ErrnoException | null, hostnames: string[]) => {
            const defaultErrorMessage: string = 'Unable to resolve hostname';
            if (error) {
                return deferred.reject(defaultErrorMessage);
            } else {
                let hostname: string = hostnames.length === 0 ? defaultErrorMessage : hostnames[0];
                hostname = hostname.replace('.localdomain', '').trim();
                deferred.resolve(hostname);
            }
        });
        return deferred.promise as q.Promise<string>;
    }

    /**
     * Update resolver configuration with current name server
     * @return { void }
     * @private
     */
    private setCurrentNameServerValue() : void {
        this.resolver.setServers([this.currentNameServer]);
    };

Solution

  • Sorry, got the question wrong so this part is not important. Proper answer below. Btw keeping it here just in case someone has a problem like this:
    That's not how DNS server lists work in almost any platform. If you look in your system network settings (Windows, Mac or Linux), surely you will see 2 nameservers (as is the default amount all ISPs provide). That's because there is a main server and a fallback server if the main one fails. You system won't return 2 different IPs when you are browsing Internet. That's weird. What your browser want is just an IP to connect to, not a list of IPs. NodeJS works the same. The rest of nameservers are just fallback ones just in case the main one fails.

    Now the proper part:
    When a nameserver replies with NOTFOUND, nodejs will not continue to the next nameserver, as is actually a working nameserver. Fallback nameservers are only used when the nameserver fails (with a weird error/response or a timeout. Documentation link below with this info).

    To achieve what you want you need to set your nameserver, make your query, wait for the response, set the next nameserver, make your query, etc... with your current code must be an easy task. Looks pretty.

    Be careful as you must not change the nameservers when a DNS query is in progress as you can read in the documentation.